abca11096c5c54279d64cf114d5a449cb2a8e02e
[jalview.git] / src / jalview / viewmodel / AlignmentViewport.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.viewmodel;
22
23 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
24 import jalview.analysis.Conservation;
25 import jalview.analysis.TreeModel;
26 import jalview.api.AlignCalcManagerI;
27 import jalview.api.AlignViewportI;
28 import jalview.api.AlignmentViewPanel;
29 import jalview.api.FeaturesDisplayedI;
30 import jalview.api.ViewStyleI;
31 import jalview.commands.CommandI;
32 import jalview.datamodel.AlignedCodonFrame;
33 import jalview.datamodel.AlignmentAnnotation;
34 import jalview.datamodel.AlignmentI;
35 import jalview.datamodel.AlignmentView;
36 import jalview.datamodel.Annotation;
37 import jalview.datamodel.ColumnSelection;
38 import jalview.datamodel.HiddenColumns;
39 import jalview.datamodel.HiddenSequences;
40 import jalview.datamodel.ProfilesI;
41 import jalview.datamodel.SearchResultsI;
42 import jalview.datamodel.Sequence;
43 import jalview.datamodel.SequenceCollectionI;
44 import jalview.datamodel.SequenceGroup;
45 import jalview.datamodel.SequenceI;
46 import jalview.renderer.ResidueShader;
47 import jalview.renderer.ResidueShaderI;
48 import jalview.schemes.ColourSchemeI;
49 import jalview.structure.CommandListener;
50 import jalview.structure.StructureSelectionManager;
51 import jalview.structure.VamsasSource;
52 import jalview.util.Comparison;
53 import jalview.util.MapList;
54 import jalview.util.MappingUtils;
55 import jalview.util.MessageManager;
56 import jalview.viewmodel.styles.ViewStyle;
57 import jalview.workers.AlignCalcManager;
58 import jalview.workers.ComplementConsensusThread;
59 import jalview.workers.ConsensusThread;
60 import jalview.workers.InformationThread;
61 import jalview.workers.StrucConsensusThread;
62
63 import java.awt.Color;
64 import java.beans.PropertyChangeSupport;
65 import java.util.ArrayDeque;
66 import java.util.ArrayList;
67 import java.util.BitSet;
68 import java.util.Deque;
69 import java.util.HashMap;
70 import java.util.Hashtable;
71 import java.util.Iterator;
72 import java.util.List;
73 import java.util.Map;
74
75 /**
76  * base class holding visualization and analysis attributes and common logic for
77  * an active alignment view displayed in the GUI
78  * 
79  * @author jimp
80  * 
81  */
82 public abstract class AlignmentViewport
83         implements AlignViewportI, CommandListener, VamsasSource
84 {
85   protected ViewportRanges ranges;
86
87   protected ViewStyleI viewStyle = new ViewStyle();
88
89   /**
90    * A viewport that hosts the cDna view of this (protein), or vice versa (if
91    * set).
92    */
93   AlignViewportI codingComplement = null;
94
95   FeaturesDisplayedI featuresDisplayed = null;
96
97   protected Deque<CommandI> historyList = new ArrayDeque<>();
98
99   protected Deque<CommandI> redoList = new ArrayDeque<>();
100
101   protected String sequenceSetID;
102
103   /*
104    * probably unused indicator that view is of a dataset rather than an
105    * alignment
106    */
107   protected boolean isDataset = false;
108
109   private Map<SequenceI, SequenceCollectionI> hiddenRepSequences;
110
111   protected ColumnSelection colSel = new ColumnSelection();
112
113   public boolean autoCalculateConsensus = true;
114
115   protected boolean autoCalculateStrucConsensus = true;
116
117   protected boolean ignoreGapsInConsensusCalculation = false;
118
119   protected boolean ignoreBelowBackGroundFrequencyCalculation = false;
120
121   protected boolean infoLetterHeight = false;
122
123   protected ResidueShaderI residueShading = new ResidueShader();
124
125   protected AlignmentAnnotation consensus;
126
127   protected AlignmentAnnotation complementConsensus;
128
129   protected AlignmentAnnotation occupancy;
130
131   protected AlignmentAnnotation strucConsensus;
132
133   protected AlignmentAnnotation conservation;
134
135   protected AlignmentAnnotation quality;
136
137   /**
138    * alignment displayed in the viewport
139    */
140   private AlignmentI alignment;
141
142   /**
143    * results of alignment consensus analysis for visible portion of view
144    */
145   protected ProfilesI consensusProfiles;
146
147   /**
148    * HMM profile for the alignment
149    */
150   protected ProfilesI hmmProfiles;
151
152   /**
153    * results of cDNA complement consensus visible portion of view
154    */
155   protected Hashtable[] hcomplementConsensus;
156
157   /**
158    * results of secondary structure base pair consensus for visible portion of
159    * view
160    */
161   protected Hashtable[] hStrucConsensus;
162
163   protected Conservation hconservation;
164
165   public AlignmentViewport(AlignmentI al)
166   {
167     setAlignment(al);
168     ranges = new ViewportRanges(al);
169   }
170
171   /**
172    * @param name
173    * @see jalview.api.ViewStyleI#setFontName(java.lang.String)
174    */
175   @Override
176   public void setFontName(String name)
177   {
178     viewStyle.setFontName(name);
179   }
180
181   /**
182    * @param style
183    * @see jalview.api.ViewStyleI#setFontStyle(int)
184    */
185   @Override
186   public void setFontStyle(int style)
187   {
188     viewStyle.setFontStyle(style);
189   }
190
191   /**
192    * @param size
193    * @see jalview.api.ViewStyleI#setFontSize(int)
194    */
195   @Override
196   public void setFontSize(int size)
197   {
198     viewStyle.setFontSize(size);
199   }
200
201   /**
202    * @return
203    * @see jalview.api.ViewStyleI#getFontStyle()
204    */
205   @Override
206   public int getFontStyle()
207   {
208     return viewStyle.getFontStyle();
209   }
210
211   /**
212    * @return
213    * @see jalview.api.ViewStyleI#getFontName()
214    */
215   @Override
216   public String getFontName()
217   {
218     return viewStyle.getFontName();
219   }
220
221   /**
222    * @return
223    * @see jalview.api.ViewStyleI#getFontSize()
224    */
225   @Override
226   public int getFontSize()
227   {
228     return viewStyle.getFontSize();
229   }
230
231   /**
232    * @param upperCasebold
233    * @see jalview.api.ViewStyleI#setUpperCasebold(boolean)
234    */
235   @Override
236   public void setUpperCasebold(boolean upperCasebold)
237   {
238     viewStyle.setUpperCasebold(upperCasebold);
239   }
240
241   /**
242    * @return
243    * @see jalview.api.ViewStyleI#isUpperCasebold()
244    */
245   @Override
246   public boolean isUpperCasebold()
247   {
248     return viewStyle.isUpperCasebold();
249   }
250
251   /**
252    * @return
253    * @see jalview.api.ViewStyleI#isSeqNameItalics()
254    */
255   @Override
256   public boolean isSeqNameItalics()
257   {
258     return viewStyle.isSeqNameItalics();
259   }
260
261   /**
262    * @param colourByReferenceSeq
263    * @see jalview.api.ViewStyleI#setColourByReferenceSeq(boolean)
264    */
265   @Override
266   public void setColourByReferenceSeq(boolean colourByReferenceSeq)
267   {
268     viewStyle.setColourByReferenceSeq(colourByReferenceSeq);
269   }
270
271   /**
272    * @param b
273    * @see jalview.api.ViewStyleI#setColourAppliesToAllGroups(boolean)
274    */
275   @Override
276   public void setColourAppliesToAllGroups(boolean b)
277   {
278     viewStyle.setColourAppliesToAllGroups(b);
279   }
280
281   /**
282    * @return
283    * @see jalview.api.ViewStyleI#getColourAppliesToAllGroups()
284    */
285   @Override
286   public boolean getColourAppliesToAllGroups()
287   {
288     return viewStyle.getColourAppliesToAllGroups();
289   }
290
291   /**
292    * @return
293    * @see jalview.api.ViewStyleI#getAbovePIDThreshold()
294    */
295   @Override
296   public boolean getAbovePIDThreshold()
297   {
298     return viewStyle.getAbovePIDThreshold();
299   }
300
301   /**
302    * @param inc
303    * @see jalview.api.ViewStyleI#setIncrement(int)
304    */
305   @Override
306   public void setIncrement(int inc)
307   {
308     viewStyle.setIncrement(inc);
309   }
310
311   /**
312    * @return
313    * @see jalview.api.ViewStyleI#getIncrement()
314    */
315   @Override
316   public int getIncrement()
317   {
318     return viewStyle.getIncrement();
319   }
320
321   /**
322    * @param b
323    * @see jalview.api.ViewStyleI#setConservationSelected(boolean)
324    */
325   @Override
326   public void setConservationSelected(boolean b)
327   {
328     viewStyle.setConservationSelected(b);
329   }
330
331   /**
332    * @param show
333    * @see jalview.api.ViewStyleI#setShowHiddenMarkers(boolean)
334    */
335   @Override
336   public void setShowHiddenMarkers(boolean show)
337   {
338     viewStyle.setShowHiddenMarkers(show);
339   }
340
341   /**
342    * @return
343    * @see jalview.api.ViewStyleI#getShowHiddenMarkers()
344    */
345   @Override
346   public boolean getShowHiddenMarkers()
347   {
348     return viewStyle.getShowHiddenMarkers();
349   }
350
351   /**
352    * @param b
353    * @see jalview.api.ViewStyleI#setScaleRightWrapped(boolean)
354    */
355   @Override
356   public void setScaleRightWrapped(boolean b)
357   {
358     viewStyle.setScaleRightWrapped(b);
359   }
360
361   /**
362    * @param b
363    * @see jalview.api.ViewStyleI#setScaleLeftWrapped(boolean)
364    */
365   @Override
366   public void setScaleLeftWrapped(boolean b)
367   {
368     viewStyle.setScaleLeftWrapped(b);
369   }
370
371   /**
372    * @param b
373    * @see jalview.api.ViewStyleI#setScaleAboveWrapped(boolean)
374    */
375   @Override
376   public void setScaleAboveWrapped(boolean b)
377   {
378     viewStyle.setScaleAboveWrapped(b);
379   }
380
381   /**
382    * @return
383    * @see jalview.api.ViewStyleI#getScaleLeftWrapped()
384    */
385   @Override
386   public boolean getScaleLeftWrapped()
387   {
388     return viewStyle.getScaleLeftWrapped();
389   }
390
391   /**
392    * @return
393    * @see jalview.api.ViewStyleI#getScaleAboveWrapped()
394    */
395   @Override
396   public boolean getScaleAboveWrapped()
397   {
398     return viewStyle.getScaleAboveWrapped();
399   }
400
401   /**
402    * @return
403    * @see jalview.api.ViewStyleI#getScaleRightWrapped()
404    */
405   @Override
406   public boolean getScaleRightWrapped()
407   {
408     return viewStyle.getScaleRightWrapped();
409   }
410
411   /**
412    * @param b
413    * @see jalview.api.ViewStyleI#setAbovePIDThreshold(boolean)
414    */
415   @Override
416   public void setAbovePIDThreshold(boolean b)
417   {
418     viewStyle.setAbovePIDThreshold(b);
419   }
420
421   /**
422    * @param thresh
423    * @see jalview.api.ViewStyleI#setThreshold(int)
424    */
425   @Override
426   public void setThreshold(int thresh)
427   {
428     viewStyle.setThreshold(thresh);
429   }
430
431   /**
432    * @return
433    * @see jalview.api.ViewStyleI#getThreshold()
434    */
435   @Override
436   public int getThreshold()
437   {
438     return viewStyle.getThreshold();
439   }
440
441   /**
442    * @return
443    * @see jalview.api.ViewStyleI#getShowJVSuffix()
444    */
445   @Override
446   public boolean getShowJVSuffix()
447   {
448     return viewStyle.getShowJVSuffix();
449   }
450
451   /**
452    * @param b
453    * @see jalview.api.ViewStyleI#setShowJVSuffix(boolean)
454    */
455   @Override
456   public void setShowJVSuffix(boolean b)
457   {
458     viewStyle.setShowJVSuffix(b);
459   }
460
461   /**
462    * @param state
463    * @see jalview.api.ViewStyleI#setWrapAlignment(boolean)
464    */
465   @Override
466   public void setWrapAlignment(boolean state)
467   {
468     viewStyle.setWrapAlignment(state);
469     ranges.setWrappedMode(state);
470   }
471
472   /**
473    * @param state
474    * @see jalview.api.ViewStyleI#setShowText(boolean)
475    */
476   @Override
477   public void setShowText(boolean state)
478   {
479     viewStyle.setShowText(state);
480   }
481
482   /**
483    * @param state
484    * @see jalview.api.ViewStyleI#setRenderGaps(boolean)
485    */
486   @Override
487   public void setRenderGaps(boolean state)
488   {
489     viewStyle.setRenderGaps(state);
490   }
491
492   /**
493    * @return
494    * @see jalview.api.ViewStyleI#getColourText()
495    */
496   @Override
497   public boolean getColourText()
498   {
499     return viewStyle.getColourText();
500   }
501
502   /**
503    * @param state
504    * @see jalview.api.ViewStyleI#setColourText(boolean)
505    */
506   @Override
507   public void setColourText(boolean state)
508   {
509     viewStyle.setColourText(state);
510   }
511
512   /**
513    * @return
514    * @see jalview.api.ViewStyleI#getWrapAlignment()
515    */
516   @Override
517   public boolean getWrapAlignment()
518   {
519     return viewStyle.getWrapAlignment();
520   }
521
522   /**
523    * @return
524    * @see jalview.api.ViewStyleI#getShowText()
525    */
526   @Override
527   public boolean getShowText()
528   {
529     return viewStyle.getShowText();
530   }
531
532   /**
533    * @return
534    * @see jalview.api.ViewStyleI#getWrappedWidth()
535    */
536   @Override
537   public int getWrappedWidth()
538   {
539     return viewStyle.getWrappedWidth();
540   }
541
542   /**
543    * @param w
544    * @see jalview.api.ViewStyleI#setWrappedWidth(int)
545    */
546   @Override
547   public void setWrappedWidth(int w)
548   {
549     viewStyle.setWrappedWidth(w);
550   }
551
552   /**
553    * @return
554    * @see jalview.api.ViewStyleI#getCharHeight()
555    */
556   @Override
557   public int getCharHeight()
558   {
559     return viewStyle.getCharHeight();
560   }
561
562   /**
563    * @param h
564    * @see jalview.api.ViewStyleI#setCharHeight(int)
565    */
566   @Override
567   public void setCharHeight(int h)
568   {
569     viewStyle.setCharHeight(h);
570   }
571
572   /**
573    * @return
574    * @see jalview.api.ViewStyleI#getCharWidth()
575    */
576   @Override
577   public int getCharWidth()
578   {
579     return viewStyle.getCharWidth();
580   }
581
582   /**
583    * @param w
584    * @see jalview.api.ViewStyleI#setCharWidth(int)
585    */
586   @Override
587   public void setCharWidth(int w)
588   {
589     viewStyle.setCharWidth(w);
590   }
591
592   /**
593    * @return
594    * @see jalview.api.ViewStyleI#getShowBoxes()
595    */
596   @Override
597   public boolean getShowBoxes()
598   {
599     return viewStyle.getShowBoxes();
600   }
601
602   /**
603    * @return
604    * @see jalview.api.ViewStyleI#getShowUnconserved()
605    */
606   @Override
607   public boolean getShowUnconserved()
608   {
609     return viewStyle.getShowUnconserved();
610   }
611
612   /**
613    * @param showunconserved
614    * @see jalview.api.ViewStyleI#setShowUnconserved(boolean)
615    */
616   @Override
617   public void setShowUnconserved(boolean showunconserved)
618   {
619     viewStyle.setShowUnconserved(showunconserved);
620   }
621
622   /**
623    * @param default1
624    * @see jalview.api.ViewStyleI#setSeqNameItalics(boolean)
625    */
626   @Override
627   public void setSeqNameItalics(boolean default1)
628   {
629     viewStyle.setSeqNameItalics(default1);
630   }
631
632   @Override
633   public AlignmentI getAlignment()
634   {
635     return alignment;
636   }
637
638   @Override
639   public char getGapCharacter()
640   {
641     return alignment.getGapCharacter();
642   }
643
644   public void setDataset(boolean b)
645   {
646     isDataset = b;
647   }
648
649   public boolean isDataset()
650   {
651     return isDataset;
652   }
653
654   @Override
655   public void setGlobalColourScheme(ColourSchemeI cs)
656   {
657     // TODO: logic refactored from AlignFrame changeColour -
658     // TODO: autorecalc stuff should be changed to rely on the worker system
659     // check to see if we should implement a changeColour(cs) method rather than
660     // put the logic in here
661     // - means that caller decides if they want to just modify state and defer
662     // calculation till later or to do all calculations in thread.
663     // via changecolour
664
665     /*
666      * only instantiate alignment colouring once, thereafter update it;
667      * this means that any conservation or PID threshold settings
668      * persist when the alignment colour scheme is changed
669      */
670     if (residueShading == null)
671     {
672       residueShading = new ResidueShader(viewStyle);
673     }
674     residueShading.setColourScheme(cs);
675
676     // TODO: do threshold and increment belong in ViewStyle or ResidueShader?
677     // ...problem: groups need these, but do not currently have a ViewStyle
678
679     if (cs != null)
680     {
681       if (getConservationSelected())
682       {
683         residueShading.setConservation(hconservation);
684       }
685       /*
686        * reset conservation flag in case just set to false if
687        * Conservation was null (calculation still in progress)
688        */
689       residueShading.setConservationApplied(getConservationSelected());
690       residueShading.alignmentChanged(alignment, hiddenRepSequences);
691     }
692
693     /*
694      * if 'apply colour to all groups' is selected... do so
695      * (but don't transfer any colour threshold settings to groups)
696      */
697     if (getColourAppliesToAllGroups())
698     {
699       for (SequenceGroup sg : getAlignment().getGroups())
700       {
701         /*
702          * retain any colour thresholds per group while
703          * changing choice of colour scheme (JAL-2386)
704          */
705         sg.setColourScheme(cs);
706         if (cs != null)
707         {
708           sg.getGroupColourScheme().alignmentChanged(sg,
709                   hiddenRepSequences);
710         }
711       }
712     }
713   }
714
715   @Override
716   public ColourSchemeI getGlobalColourScheme()
717   {
718     return residueShading == null ? null : residueShading.getColourScheme();
719   }
720
721   @Override
722   public ResidueShaderI getResidueShading()
723   {
724     return residueShading;
725   }
726   @Override
727   public void setConservation(Conservation cons)
728   {
729     hconservation = cons;
730   }
731
732   /**
733    * percentage gaps allowed in a column before all amino acid properties should
734    * be considered unconserved
735    */
736   int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
737
738   @Override
739   public int getConsPercGaps()
740   {
741     return ConsPercGaps;
742   }
743
744   @Override
745   public void setConsensusProfiles(ProfilesI hconsensus)
746   {
747     this.consensusProfiles = hconsensus;
748   }
749
750   @Override
751   public void setComplementConsensusHash(Hashtable[] hconsensus)
752   {
753     this.hcomplementConsensus = hconsensus;
754   }
755
756   @Override
757   public ProfilesI getConsensusProfiles()
758   {
759     return consensusProfiles;
760   }
761
762   @Override
763   public void setHmmProfiles(ProfilesI info)
764   {
765     hmmProfiles = info;
766   }
767
768   @Override
769   public ProfilesI getHmmProfiles()
770   {
771     return hmmProfiles;
772   }
773
774   @Override
775   public Hashtable[] getComplementConsensusHash()
776   {
777     return hcomplementConsensus;
778   }
779
780   @Override
781   public Hashtable[] getRnaStructureConsensusHash()
782   {
783     return hStrucConsensus;
784   }
785
786   @Override
787   public void setRnaStructureConsensusHash(Hashtable[] hStrucConsensus)
788   {
789     this.hStrucConsensus = hStrucConsensus;
790
791   }
792
793   @Override
794   public AlignmentAnnotation getAlignmentQualityAnnot()
795   {
796     return quality;
797   }
798
799   @Override
800   public AlignmentAnnotation getAlignmentConservationAnnotation()
801   {
802     return conservation;
803   }
804
805   @Override
806   public AlignmentAnnotation getAlignmentConsensusAnnotation()
807   {
808     return consensus;
809   }
810
811   @Override
812   public AlignmentAnnotation getOccupancyAnnotation()
813   {
814     return occupancy;
815   }
816
817   @Override
818   public AlignmentAnnotation getComplementConsensusAnnotation()
819   {
820     return complementConsensus;
821   }
822
823   @Override
824   public AlignmentAnnotation getAlignmentStrucConsensusAnnotation()
825   {
826     return strucConsensus;
827   }
828
829   protected AlignCalcManagerI calculator = new AlignCalcManager();
830
831   /**
832    * trigger update of conservation annotation
833    */
834   public void updateConservation(final AlignmentViewPanel ap)
835   {
836     // see note in mantis : issue number 8585
837     if (alignment.isNucleotide()
838             || (conservation == null && quality == null)
839             || !autoCalculateConsensus)
840     {
841       return;
842     }
843     if (calculator.getRegisteredWorkersOfClass(
844             jalview.workers.ConservationThread.class) == null)
845     {
846       calculator.registerWorker(
847               new jalview.workers.ConservationThread(this, ap));
848     }
849   }
850
851   /**
852    * trigger update of consensus annotation
853    */
854   public void updateConsensus(final AlignmentViewPanel ap)
855   {
856     // see note in mantis : issue number 8585
857     if (consensus == null || !autoCalculateConsensus)
858     {
859       return;
860     }
861     if (calculator
862             .getRegisteredWorkersOfClass(ConsensusThread.class) == null)
863     {
864       calculator.registerWorker(new ConsensusThread(this, ap));
865     }
866
867     /*
868      * A separate thread to compute cDNA consensus for a protein alignment
869      * which has mapping to cDNA
870      */
871     final AlignmentI al = this.getAlignment();
872     if (!al.isNucleotide() && al.getCodonFrames() != null
873             && !al.getCodonFrames().isEmpty())
874     {
875       /*
876        * fudge - check first for protein-to-nucleotide mappings
877        * (we don't want to do this for protein-to-protein)
878        */
879       boolean doConsensus = false;
880       for (AlignedCodonFrame mapping : al.getCodonFrames())
881       {
882         // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame?
883         MapList[] mapLists = mapping.getdnaToProt();
884         // mapLists can be empty if project load has not finished resolving seqs
885         if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
886         {
887           doConsensus = true;
888           break;
889         }
890       }
891       if (doConsensus)
892       {
893         if (calculator.getRegisteredWorkersOfClass(
894                 ComplementConsensusThread.class) == null)
895         {
896           calculator
897                   .registerWorker(new ComplementConsensusThread(this, ap));
898         }
899       }
900     }
901   }
902
903   @Override
904   public void initInformationWorker(final AlignmentViewPanel ap)
905   {
906     if (calculator
907             .getRegisteredWorkersOfClass(InformationThread.class) == null)
908     {
909       calculator.registerWorker(new InformationThread(this, ap));
910     }
911   }
912
913   // --------START Structure Conservation
914   public void updateStrucConsensus(final AlignmentViewPanel ap)
915   {
916     if (autoCalculateStrucConsensus && strucConsensus == null
917             && alignment.isNucleotide() && alignment.hasRNAStructure())
918     {
919       // secondary structure has been added - so init the consensus line
920       initRNAStructure();
921     }
922
923     // see note in mantis : issue number 8585
924     if (strucConsensus == null || !autoCalculateStrucConsensus)
925     {
926       return;
927     }
928     if (calculator.getRegisteredWorkersOfClass(
929             StrucConsensusThread.class) == null)
930     {
931       calculator.registerWorker(new StrucConsensusThread(this, ap));
932     }
933   }
934
935   public boolean isCalcInProgress()
936   {
937     return calculator.isWorking();
938   }
939
940   @Override
941   public boolean isCalculationInProgress(
942           AlignmentAnnotation alignmentAnnotation)
943   {
944     if (!alignmentAnnotation.autoCalculated)
945     {
946       return false;
947     }
948     if (calculator.workingInvolvedWith(alignmentAnnotation))
949     {
950       // System.err.println("grey out ("+alignmentAnnotation.label+")");
951       return true;
952     }
953     return false;
954   }
955
956   public void setAlignment(AlignmentI align)
957   {
958     this.alignment = align;
959   }
960
961   /**
962    * Clean up references when this viewport is closed
963    */
964   @Override
965   public void dispose()
966   {
967     /*
968      * defensively null out references to large objects in case
969      * this object is not garbage collected (as if!)
970      */
971     consensus = null;
972     complementConsensus = null;
973     strucConsensus = null;
974     conservation = null;
975     quality = null;
976     consensusProfiles = null;
977     hconservation = null;
978     hcomplementConsensus = null;
979     occupancy = null;
980     calculator = null;
981     residueShading = null; // may hold a reference to Consensus
982     changeSupport = null;
983     ranges = null;
984     currentTree = null;
985     selectionGroup = null;
986     setAlignment(null);
987   }
988
989   @Override
990   public boolean isClosed()
991   {
992     // TODO: check that this isClosed is only true after panel is closed, not
993     // before it is fully constructed.
994     return alignment == null;
995   }
996
997   @Override
998   public AlignCalcManagerI getCalcManager()
999   {
1000     return calculator;
1001   }
1002
1003   /**
1004    * should conservation rows be shown for groups
1005    */
1006   protected boolean showGroupConservation = false;
1007
1008   /**
1009    * should consensus rows be shown for groups
1010    */
1011   protected boolean showGroupConsensus = false;
1012
1013   /**
1014    * should consensus profile be rendered by default
1015    */
1016   protected boolean showSequenceLogo = false;
1017
1018   /**
1019    * should consensus profile be rendered normalised to row height
1020    */
1021   protected boolean normaliseSequenceLogo = false;
1022
1023   /**
1024    * should consensus histograms be rendered by default
1025    */
1026   protected boolean showConsensusHistogram = true;
1027
1028   /**
1029    * should hmm profile be rendered by default
1030    */
1031   protected boolean hmmShowSequenceLogo = false;
1032
1033   /**
1034    * should hmm profile be rendered normalised to row height
1035    */
1036   protected boolean hmmNormaliseSequenceLogo = false;
1037
1038   /**
1039    * should information histograms be rendered by default
1040    */
1041   protected boolean hmmShowHistogram = true;
1042
1043   /**
1044    * @return the showConsensusProfile
1045    */
1046   @Override
1047   public boolean isShowSequenceLogo()
1048   {
1049     return showSequenceLogo;
1050   }
1051
1052   /**
1053    * @return the showInformationProfile
1054    */
1055   @Override
1056   public boolean isShowHMMSequenceLogo()
1057   {
1058     return hmmShowSequenceLogo;
1059   }
1060
1061   /**
1062    * @param showSequenceLogo
1063    *          the new value
1064    */
1065   public void setShowSequenceLogo(boolean showSequenceLogo)
1066   {
1067     if (showSequenceLogo != this.showSequenceLogo)
1068     {
1069       // TODO: decouple settings setting from calculation when refactoring
1070       // annotation update method from alignframe to viewport
1071       this.showSequenceLogo = showSequenceLogo;
1072       calculator.updateAnnotationFor(ConsensusThread.class);
1073       calculator.updateAnnotationFor(ComplementConsensusThread.class);
1074       calculator.updateAnnotationFor(StrucConsensusThread.class);
1075     }
1076     this.showSequenceLogo = showSequenceLogo;
1077   }
1078
1079   public void setShowHMMSequenceLogo(boolean showHMMSequenceLogo)
1080   {
1081     if (showHMMSequenceLogo != this.hmmShowSequenceLogo)
1082     {
1083       this.hmmShowSequenceLogo = showHMMSequenceLogo;
1084       // TODO: updateAnnotation if description (tooltip) will show
1085       // profile in place of information content?
1086       // calculator.updateAnnotationFor(InformationThread.class);
1087     }
1088     this.hmmShowSequenceLogo = showHMMSequenceLogo;
1089   }
1090
1091   /**
1092    * @param showConsensusHistogram
1093    *          the showConsensusHistogram to set
1094    */
1095   public void setShowConsensusHistogram(boolean showConsensusHistogram)
1096   {
1097     this.showConsensusHistogram = showConsensusHistogram;
1098   }
1099
1100   /**
1101    * @param showInformationHistogram
1102    */
1103   public void setShowInformationHistogram(boolean showInformationHistogram)
1104   {
1105     this.hmmShowHistogram = showInformationHistogram;
1106   }
1107
1108   /**
1109    * @return the showGroupConservation
1110    */
1111   public boolean isShowGroupConservation()
1112   {
1113     return showGroupConservation;
1114   }
1115
1116   /**
1117    * @param showGroupConservation
1118    *          the showGroupConservation to set
1119    */
1120   public void setShowGroupConservation(boolean showGroupConservation)
1121   {
1122     this.showGroupConservation = showGroupConservation;
1123   }
1124
1125   /**
1126    * @return the showGroupConsensus
1127    */
1128   public boolean isShowGroupConsensus()
1129   {
1130     return showGroupConsensus;
1131   }
1132
1133   /**
1134    * @param showGroupConsensus
1135    *          the showGroupConsensus to set
1136    */
1137   public void setShowGroupConsensus(boolean showGroupConsensus)
1138   {
1139     this.showGroupConsensus = showGroupConsensus;
1140   }
1141
1142   /**
1143    * 
1144    * @return flag to indicate if the consensus histogram should be rendered by
1145    *         default
1146    */
1147   @Override
1148   public boolean isShowConsensusHistogram()
1149   {
1150     return this.showConsensusHistogram;
1151   }
1152
1153   /**
1154    * 
1155    * @return flag to indicate if the information content histogram should be
1156    *         rendered by default
1157    */
1158   @Override
1159   public boolean isShowInformationHistogram()
1160   {
1161     return this.hmmShowHistogram;
1162   }
1163
1164   /**
1165    * when set, updateAlignment will always ensure sequences are of equal length
1166    */
1167   private boolean padGaps = false;
1168
1169   /**
1170    * when set, alignment should be reordered according to a newly opened tree
1171    */
1172   public boolean sortByTree = false;
1173
1174   /**
1175    * 
1176    * 
1177    * @return null or the currently selected sequence region
1178    */
1179   @Override
1180   public SequenceGroup getSelectionGroup()
1181   {
1182     return selectionGroup;
1183   }
1184
1185   /**
1186    * Set the selection group for this window. Also sets the current alignment as
1187    * the context for the group, if it does not already have one.
1188    * 
1189    * @param sg
1190    *          - group holding references to sequences in this alignment view
1191    * 
1192    */
1193   @Override
1194   public void setSelectionGroup(SequenceGroup sg)
1195   {
1196     selectionGroup = sg;
1197     if (sg != null && sg.getContext() == null)
1198     {
1199       sg.setContext(alignment);
1200     }
1201   }
1202
1203   public void setHiddenColumns(HiddenColumns hidden)
1204   {
1205     this.alignment.setHiddenColumns(hidden);
1206   }
1207
1208   @Override
1209   public ColumnSelection getColumnSelection()
1210   {
1211     return colSel;
1212   }
1213
1214   @Override
1215   public void setColumnSelection(ColumnSelection colSel)
1216   {
1217     this.colSel = colSel;
1218     if (colSel != null)
1219     {
1220       updateHiddenColumns();
1221     }
1222     isColSelChanged(true);
1223   }
1224
1225   /**
1226    * 
1227    * @return
1228    */
1229   @Override
1230   public Map<SequenceI, SequenceCollectionI> getHiddenRepSequences()
1231   {
1232     return hiddenRepSequences;
1233   }
1234
1235   @Override
1236   public void setHiddenRepSequences(
1237           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
1238   {
1239     this.hiddenRepSequences = hiddenRepSequences;
1240   }
1241
1242   @Override
1243   public boolean hasSelectedColumns()
1244   {
1245     ColumnSelection columnSelection = getColumnSelection();
1246     return columnSelection != null && columnSelection.hasSelectedColumns();
1247   }
1248
1249   @Override
1250   public boolean hasHiddenColumns()
1251   {
1252     return alignment.getHiddenColumns() != null
1253             && alignment.getHiddenColumns().hasHiddenColumns();
1254   }
1255
1256   public void updateHiddenColumns()
1257   {
1258     // this method doesn't really do anything now. But - it could, since a
1259     // column Selection could be in the process of modification
1260     // hasHiddenColumns = colSel.hasHiddenColumns();
1261   }
1262
1263   @Override
1264   public boolean hasHiddenRows()
1265   {
1266     return alignment.getHiddenSequences().getSize() > 0;
1267   }
1268
1269   protected SequenceGroup selectionGroup;
1270
1271   public void setSequenceSetId(String newid)
1272   {
1273     if (sequenceSetID != null)
1274     {
1275       System.err.println(
1276               "Warning - overwriting a sequenceSetId for a viewport!");
1277     }
1278     sequenceSetID = new String(newid);
1279   }
1280
1281   @Override
1282   public String getSequenceSetId()
1283   {
1284     if (sequenceSetID == null)
1285     {
1286       sequenceSetID = alignment.hashCode() + "";
1287     }
1288
1289     return sequenceSetID;
1290   }
1291
1292   /**
1293    * unique viewId for synchronizing state (e.g. with stored Jalview Project)
1294    * 
1295    */
1296   protected String viewId = null;
1297
1298   @Override
1299   public String getViewId()
1300   {
1301     if (viewId == null)
1302     {
1303       viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
1304     }
1305     return viewId;
1306   }
1307
1308   public void setIgnoreGapsConsensus(boolean b, AlignmentViewPanel ap)
1309   {
1310     ignoreGapsInConsensusCalculation = b;
1311     if (ap != null)
1312     {
1313       if (residueShading != null)
1314       {
1315         residueShading.setThreshold(residueShading.getThreshold(),
1316                 ignoreGapsInConsensusCalculation);
1317       }
1318     }
1319   }
1320
1321   public void setIgnoreBelowBackground(boolean b, AlignmentViewPanel ap)
1322   {
1323     ignoreBelowBackGroundFrequencyCalculation = b;
1324   }
1325
1326   public void setInfoLetterHeight(boolean b, AlignmentViewPanel ap)
1327   {
1328     infoLetterHeight = b;
1329   }
1330
1331   private long sgrouphash = -1, colselhash = -1;
1332
1333   /**
1334    * checks current SelectionGroup against record of last hash value, and
1335    * updates record.
1336    * 
1337    * @param b
1338    *          update the record of last hash value
1339    * 
1340    * @return true if SelectionGroup changed since last call (when b is true)
1341    */
1342   public boolean isSelectionGroupChanged(boolean b)
1343   {
1344     int hc = (selectionGroup == null || selectionGroup.getSize() == 0) ? -1
1345             : selectionGroup.hashCode();
1346     if (hc != -1 && hc != sgrouphash)
1347     {
1348       if (b)
1349       {
1350         sgrouphash = hc;
1351       }
1352       return true;
1353     }
1354     return false;
1355   }
1356
1357   /**
1358    * checks current colsel against record of last hash value, and optionally
1359    * updates record.
1360    * 
1361    * @param b
1362    *          update the record of last hash value
1363    * @return true if colsel changed since last call (when b is true)
1364    */
1365   public boolean isColSelChanged(boolean b)
1366   {
1367     int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel.hashCode();
1368     if (hc != -1 && hc != colselhash)
1369     {
1370       if (b)
1371       {
1372         colselhash = hc;
1373       }
1374       return true;
1375     }
1376     return false;
1377   }
1378
1379   @Override
1380   public boolean isIgnoreGapsConsensus()
1381   {
1382     return ignoreGapsInConsensusCalculation;
1383   }
1384
1385   @Override
1386   public boolean isIgnoreBelowBackground()
1387   {
1388     return ignoreBelowBackGroundFrequencyCalculation;
1389   }
1390
1391   @Override
1392   public boolean isInfoLetterHeight()
1393   {
1394     return infoLetterHeight;
1395   }
1396
1397   // property change stuff
1398   // JBPNote Prolly only need this in the applet version.
1399   private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
1400           this);
1401
1402   protected boolean showConservation = true;
1403
1404   protected boolean showQuality = true;
1405
1406   protected boolean showConsensus = true;
1407
1408   protected boolean showOccupancy = true;
1409
1410   private Map<SequenceI, Color> sequenceColours = new HashMap<>();
1411
1412   protected SequenceAnnotationOrder sortAnnotationsBy = null;
1413
1414   protected boolean showAutocalculatedAbove;
1415
1416   /**
1417    * when set, view will scroll to show the highlighted position
1418    */
1419   private boolean followHighlight = true;
1420
1421   /**
1422    * Property change listener for changes in alignment
1423    * 
1424    * @param listener
1425    *          DOCUMENT ME!
1426    */
1427   public void addPropertyChangeListener(
1428           java.beans.PropertyChangeListener listener)
1429   {
1430     changeSupport.addPropertyChangeListener(listener);
1431   }
1432
1433   /**
1434    * DOCUMENT ME!
1435    * 
1436    * @param listener
1437    *          DOCUMENT ME!
1438    */
1439   public void removePropertyChangeListener(
1440           java.beans.PropertyChangeListener listener)
1441   {
1442     if (changeSupport != null)
1443     {
1444       changeSupport.removePropertyChangeListener(listener);
1445     }
1446   }
1447
1448   /**
1449    * Property change listener for changes in alignment
1450    * 
1451    * @param prop
1452    *          DOCUMENT ME!
1453    * @param oldvalue
1454    *          DOCUMENT ME!
1455    * @param newvalue
1456    *          DOCUMENT ME!
1457    */
1458   public void firePropertyChange(String prop, Object oldvalue,
1459           Object newvalue)
1460   {
1461     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
1462   }
1463
1464   // common hide/show column stuff
1465
1466   public void hideSelectedColumns()
1467   {
1468     if (colSel.isEmpty())
1469     {
1470       return;
1471     }
1472
1473     colSel.hideSelectedColumns(alignment);
1474     setSelectionGroup(null);
1475     isColSelChanged(true);
1476   }
1477
1478   public void hideColumns(int start, int end)
1479   {
1480     if (start == end)
1481     {
1482       colSel.hideSelectedColumns(start, alignment.getHiddenColumns());
1483     }
1484     else
1485     {
1486       alignment.getHiddenColumns().hideColumns(start, end);
1487     }
1488     isColSelChanged(true);
1489   }
1490
1491   public void showColumn(int col)
1492   {
1493     alignment.getHiddenColumns().revealHiddenColumns(col, colSel);
1494     isColSelChanged(true);
1495   }
1496
1497   public void showAllHiddenColumns()
1498   {
1499     alignment.getHiddenColumns().revealAllHiddenColumns(colSel);
1500     isColSelChanged(true);
1501   }
1502
1503   // common hide/show seq stuff
1504   public void showAllHiddenSeqs()
1505   {
1506     int startSeq = ranges.getStartSeq();
1507     int endSeq = ranges.getEndSeq();
1508
1509     if (alignment.getHiddenSequences().getSize() > 0)
1510     {
1511       if (selectionGroup == null)
1512       {
1513         selectionGroup = new SequenceGroup();
1514         selectionGroup.setEndRes(alignment.getWidth() - 1);
1515       }
1516       List<SequenceI> tmp = alignment.getHiddenSequences()
1517               .showAll(hiddenRepSequences);
1518       for (SequenceI seq : tmp)
1519       {
1520         selectionGroup.addSequence(seq, false);
1521         setSequenceAnnotationsVisible(seq, true);
1522       }
1523
1524       hiddenRepSequences = null;
1525
1526       ranges.setStartEndSeq(startSeq, endSeq + tmp.size());
1527
1528       firePropertyChange("alignment", null, alignment.getSequences());
1529       // used to set hasHiddenRows/hiddenRepSequences here, after the property
1530       // changed event
1531       sendSelection();
1532     }
1533   }
1534
1535   public void showSequence(int index)
1536   {
1537     int startSeq = ranges.getStartSeq();
1538     int endSeq = ranges.getEndSeq();
1539
1540     List<SequenceI> tmp = alignment.getHiddenSequences().showSequence(index,
1541             hiddenRepSequences);
1542     if (tmp.size() > 0)
1543     {
1544       if (selectionGroup == null)
1545       {
1546         selectionGroup = new SequenceGroup();
1547         selectionGroup.setEndRes(alignment.getWidth() - 1);
1548       }
1549
1550       for (SequenceI seq : tmp)
1551       {
1552         selectionGroup.addSequence(seq, false);
1553         setSequenceAnnotationsVisible(seq, true);
1554       }
1555
1556       ranges.setStartEndSeq(startSeq, endSeq + tmp.size());
1557
1558       firePropertyChange("alignment", null, alignment.getSequences());
1559       sendSelection();
1560     }
1561   }
1562
1563   public void hideAllSelectedSeqs()
1564   {
1565     if (selectionGroup == null || selectionGroup.getSize() < 1)
1566     {
1567       return;
1568     }
1569
1570     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
1571
1572     hideSequence(seqs);
1573
1574     setSelectionGroup(null);
1575   }
1576
1577   public void hideSequence(SequenceI[] seq)
1578   {
1579     /*
1580      * cache offset to first visible sequence
1581      */
1582     int startSeq = ranges.getStartSeq();
1583
1584     if (seq != null)
1585     {
1586       for (int i = 0; i < seq.length; i++)
1587       {
1588         alignment.getHiddenSequences().hideSequence(seq[i]);
1589         setSequenceAnnotationsVisible(seq[i], false);
1590       }
1591       ranges.setStartSeq(startSeq);
1592       firePropertyChange("alignment", null, alignment.getSequences());
1593     }
1594   }
1595
1596   /**
1597    * Hides the specified sequence, or the sequences it represents
1598    * 
1599    * @param sequence
1600    *          the sequence to hide, or keep as representative
1601    * @param representGroup
1602    *          if true, hide the current selection group except for the
1603    *          representative sequence
1604    */
1605   public void hideSequences(SequenceI sequence, boolean representGroup)
1606   {
1607     if (selectionGroup == null || selectionGroup.getSize() < 1)
1608     {
1609       hideSequence(new SequenceI[] { sequence });
1610       return;
1611     }
1612
1613     if (representGroup)
1614     {
1615       hideRepSequences(sequence, selectionGroup);
1616       setSelectionGroup(null);
1617       return;
1618     }
1619
1620     int gsize = selectionGroup.getSize();
1621     SequenceI[] hseqs = selectionGroup.getSequences()
1622             .toArray(new SequenceI[gsize]);
1623
1624     hideSequence(hseqs);
1625     setSelectionGroup(null);
1626     sendSelection();
1627   }
1628
1629   /**
1630    * Set visibility for any annotations for the given sequence.
1631    * 
1632    * @param sequenceI
1633    */
1634   protected void setSequenceAnnotationsVisible(SequenceI sequenceI,
1635           boolean visible)
1636   {
1637     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
1638     if (anns != null)
1639     {
1640       for (AlignmentAnnotation ann : anns)
1641       {
1642         if (ann.sequenceRef == sequenceI)
1643         {
1644           ann.visible = visible;
1645         }
1646       }
1647     }
1648   }
1649
1650   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
1651   {
1652     int sSize = sg.getSize();
1653     if (sSize < 2)
1654     {
1655       return;
1656     }
1657
1658     if (hiddenRepSequences == null)
1659     {
1660       hiddenRepSequences = new Hashtable<>();
1661     }
1662
1663     hiddenRepSequences.put(repSequence, sg);
1664
1665     // Hide all sequences except the repSequence
1666     SequenceI[] seqs = new SequenceI[sSize - 1];
1667     int index = 0;
1668     for (int i = 0; i < sSize; i++)
1669     {
1670       if (sg.getSequenceAt(i) != repSequence)
1671       {
1672         if (index == sSize - 1)
1673         {
1674           return;
1675         }
1676
1677         seqs[index++] = sg.getSequenceAt(i);
1678       }
1679     }
1680     sg.setSeqrep(repSequence); // note: not done in 2.7applet
1681     sg.setHidereps(true); // note: not done in 2.7applet
1682     hideSequence(seqs);
1683
1684   }
1685
1686   /**
1687    * 
1688    * @return null or the current reference sequence
1689    */
1690   public SequenceI getReferenceSeq()
1691   {
1692     return alignment.getSeqrep();
1693   }
1694
1695   /**
1696    * @param seq
1697    * @return true iff seq is the reference for the alignment
1698    */
1699   public boolean isReferenceSeq(SequenceI seq)
1700   {
1701     return alignment.getSeqrep() == seq;
1702   }
1703
1704   /**
1705    * 
1706    * @param seq
1707    * @return true if there are sequences represented by this sequence that are
1708    *         currently hidden
1709    */
1710   public boolean isHiddenRepSequence(SequenceI seq)
1711   {
1712     return (hiddenRepSequences != null
1713             && hiddenRepSequences.containsKey(seq));
1714   }
1715
1716   /**
1717    * 
1718    * @param seq
1719    * @return null or a sequence group containing the sequences that seq
1720    *         represents
1721    */
1722   public SequenceGroup getRepresentedSequences(SequenceI seq)
1723   {
1724     return (SequenceGroup) (hiddenRepSequences == null ? null
1725             : hiddenRepSequences.get(seq));
1726   }
1727
1728   @Override
1729   public int adjustForHiddenSeqs(int alignmentIndex)
1730   {
1731     return alignment.getHiddenSequences()
1732             .adjustForHiddenSeqs(alignmentIndex);
1733   }
1734
1735   @Override
1736   public void invertColumnSelection()
1737   {
1738     colSel.invertColumnSelection(0, alignment.getWidth(), alignment);
1739   }
1740
1741   @Override
1742   public SequenceI[] getSelectionAsNewSequence()
1743   {
1744     SequenceI[] sequences;
1745     // JBPNote: Need to test jalviewLite.getSelectedSequencesAsAlignmentFrom -
1746     // this was the only caller in the applet for this method
1747     // JBPNote: in applet, this method returned references to the alignment
1748     // sequences, and it did not honour the presence/absence of annotation
1749     // attached to the alignment (probably!)
1750     if (selectionGroup == null || selectionGroup.getSize() == 0)
1751     {
1752       sequences = alignment.getSequencesArray();
1753       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
1754       for (int i = 0; i < sequences.length; i++)
1755       {
1756         // construct new sequence with subset of visible annotation
1757         sequences[i] = new Sequence(sequences[i], annots);
1758       }
1759     }
1760     else
1761     {
1762       sequences = selectionGroup.getSelectionAsNewSequences(alignment,
1763               true);
1764     }
1765
1766     return sequences;
1767   }
1768
1769   @Override
1770   public SequenceI[] getSequenceSelection()
1771   {
1772     SequenceI[] sequences = null;
1773     if (selectionGroup != null)
1774     {
1775       sequences = selectionGroup.getSequencesInOrder(alignment);
1776     }
1777     if (sequences == null)
1778     {
1779       sequences = alignment.getSequencesArray();
1780     }
1781     return sequences;
1782   }
1783
1784   @Override
1785   public jalview.datamodel.AlignmentView getAlignmentView(
1786           boolean selectedOnly)
1787   {
1788     return getAlignmentView(selectedOnly, false);
1789   }
1790
1791   @Override
1792   public jalview.datamodel.AlignmentView getAlignmentView(
1793           boolean selectedOnly, boolean markGroups)
1794   {
1795     return new AlignmentView(alignment, alignment.getHiddenColumns(),
1796             selectionGroup,
1797             alignment.getHiddenColumns() != null
1798                     && alignment.getHiddenColumns().hasHiddenColumns(),
1799             selectedOnly, markGroups);
1800   }
1801
1802   @Override
1803   public String[] getViewAsString(boolean selectedRegionOnly)
1804   {
1805     return getViewAsString(selectedRegionOnly, true);
1806   }
1807
1808   @Override
1809   public String[] getViewAsString(boolean selectedRegionOnly,
1810           boolean exportHiddenSeqs)
1811   {
1812     String[] selection = null;
1813     SequenceI[] seqs = null;
1814     int i, iSize;
1815     int start = 0, end = 0;
1816     if (selectedRegionOnly && selectionGroup != null)
1817     {
1818       iSize = selectionGroup.getSize();
1819       seqs = selectionGroup.getSequencesInOrder(alignment);
1820       start = selectionGroup.getStartRes();
1821       end = selectionGroup.getEndRes() + 1;
1822     }
1823     else
1824     {
1825       if (hasHiddenRows() && exportHiddenSeqs)
1826       {
1827         AlignmentI fullAlignment = alignment.getHiddenSequences()
1828                 .getFullAlignment();
1829         iSize = fullAlignment.getHeight();
1830         seqs = fullAlignment.getSequencesArray();
1831         end = fullAlignment.getWidth();
1832       }
1833       else
1834       {
1835         iSize = alignment.getHeight();
1836         seqs = alignment.getSequencesArray();
1837         end = alignment.getWidth();
1838       }
1839     }
1840
1841     selection = new String[iSize];
1842     if (alignment.getHiddenColumns() != null
1843             && alignment.getHiddenColumns().hasHiddenColumns())
1844     {
1845       for (i = 0; i < iSize; i++)
1846       {
1847         Iterator<int[]> blocks = alignment.getHiddenColumns()
1848                 .getVisContigsIterator(start, end + 1, false);
1849         selection[i] = seqs[i].getSequenceStringFromIterator(blocks);
1850       }
1851     }
1852     else
1853     {
1854       for (i = 0; i < iSize; i++)
1855       {
1856         selection[i] = seqs[i].getSequenceAsString(start, end);
1857       }
1858
1859     }
1860     return selection;
1861   }
1862
1863   @Override
1864   public List<int[]> getVisibleRegionBoundaries(int min, int max)
1865   {
1866     ArrayList<int[]> regions = new ArrayList<>();
1867     int start = min;
1868     int end = max;
1869
1870     do
1871     {
1872       HiddenColumns hidden = alignment.getHiddenColumns();
1873       if (hidden != null && hidden.hasHiddenColumns())
1874       {
1875         if (start == 0)
1876         {
1877           start = hidden.visibleToAbsoluteColumn(start);
1878         }
1879
1880         end = hidden.getNextHiddenBoundary(false, start);
1881         if (start == end)
1882         {
1883           end = max;
1884         }
1885         if (end > max)
1886         {
1887           end = max;
1888         }
1889       }
1890
1891       regions.add(new int[] { start, end });
1892
1893       if (hidden != null && hidden.hasHiddenColumns())
1894       {
1895         start = hidden.visibleToAbsoluteColumn(end);
1896         start = hidden.getNextHiddenBoundary(true, start) + 1;
1897       }
1898     } while (end < max);
1899
1900     int[][] startEnd = new int[regions.size()][2];
1901
1902     return regions;
1903   }
1904
1905   @Override
1906   public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(
1907           boolean selectedOnly)
1908   {
1909     ArrayList<AlignmentAnnotation> ala = new ArrayList<>();
1910     AlignmentAnnotation[] aa;
1911     if ((aa = alignment.getAlignmentAnnotation()) != null)
1912     {
1913       for (AlignmentAnnotation annot : aa)
1914       {
1915         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
1916         if (selectedOnly && selectionGroup != null)
1917         {
1918           clone.makeVisibleAnnotation(
1919                   selectionGroup.getStartRes(), selectionGroup.getEndRes(),
1920                   alignment.getHiddenColumns());
1921         }
1922         else
1923         {
1924           clone.makeVisibleAnnotation(alignment.getHiddenColumns());
1925         }
1926         ala.add(clone);
1927       }
1928     }
1929     return ala;
1930   }
1931
1932   @Override
1933   public boolean isPadGaps()
1934   {
1935     return padGaps;
1936   }
1937
1938   @Override
1939   public void setPadGaps(boolean padGaps)
1940   {
1941     this.padGaps = padGaps;
1942   }
1943
1944   /**
1945    * apply any post-edit constraints and trigger any calculations needed after
1946    * an edit has been performed on the alignment
1947    * 
1948    * @param ap
1949    */
1950   @Override
1951   public void alignmentChanged(AlignmentViewPanel ap)
1952   {
1953     if (isPadGaps())
1954     {
1955       alignment.padGaps();
1956     }
1957
1958     // Reset endRes of groups if beyond alignment width
1959     int alWidth = alignment.getWidth();
1960     List<SequenceGroup> groups = alignment.getGroups();
1961     if (groups != null)
1962     {
1963       for (SequenceGroup sg : groups)
1964       {
1965         if (sg.getEndRes() > alWidth)
1966         {
1967           sg.setEndRes(alWidth - 1);
1968         }
1969       }
1970     }
1971
1972     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1973     {
1974       selectionGroup.setEndRes(alWidth - 1);
1975     }
1976
1977     updateAllColourSchemes();
1978     calculator.restartWorkers();
1979   }
1980
1981   /**
1982    * reset scope and do calculations for all applied colourschemes on alignment
1983    */
1984   void updateAllColourSchemes()
1985   {
1986     ResidueShaderI rs = residueShading;
1987     if (rs != null)
1988     {
1989       rs.alignmentChanged(alignment, hiddenRepSequences);
1990
1991       rs.setConsensus(consensusProfiles);
1992       if (rs.conservationApplied())
1993       {
1994         rs.setConservation(Conservation.calculateConservation("All",
1995                 alignment.getSequences(), 0, alignment.getWidth(), false,
1996                 getConsPercGaps(), false));
1997       }
1998     }
1999
2000     for (SequenceGroup sg : alignment.getGroups())
2001     {
2002       if (sg.cs != null)
2003       {
2004         sg.cs.alignmentChanged(sg, hiddenRepSequences);
2005       }
2006       sg.recalcConservation();
2007     }
2008   }
2009
2010   protected void initAutoAnnotation()
2011   {
2012     // TODO: add menu option action that nulls or creates consensus object
2013     // depending on if the user wants to see the annotation or not in a
2014     // specific alignment
2015
2016     if (consensusProfiles == null && !isDataset)
2017     {
2018       if (!alignment.isNucleotide())
2019       {
2020         initConservation();
2021         initQuality();
2022       }
2023       else
2024       {
2025         initRNAStructure();
2026       }
2027       consensus = new AlignmentAnnotation("Consensus",
2028               MessageManager.getString("label.consensus_descr"),
2029               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
2030       initConsensus(consensus);
2031
2032       initOccupancy();
2033
2034       initComplementConsensus();
2035     }
2036   }
2037
2038   /**
2039    * If this is a protein alignment and there are mappings to cDNA, adds the
2040    * cDNA consensus annotation and returns true, else returns false.
2041    */
2042   public boolean initComplementConsensus()
2043   {
2044     if (!alignment.isNucleotide())
2045     {
2046       final List<AlignedCodonFrame> codonMappings = alignment
2047               .getCodonFrames();
2048       if (codonMappings != null && !codonMappings.isEmpty())
2049       {
2050         boolean doConsensus = false;
2051         for (AlignedCodonFrame mapping : codonMappings)
2052         {
2053           // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame?
2054           MapList[] mapLists = mapping.getdnaToProt();
2055           // mapLists can be empty if project load has not finished resolving
2056           // seqs
2057           if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
2058           {
2059             doConsensus = true;
2060             break;
2061           }
2062         }
2063         if (doConsensus)
2064         {
2065           complementConsensus = new AlignmentAnnotation("cDNA Consensus",
2066                   MessageManager
2067                           .getString("label.complement_consensus_descr"),
2068                   new Annotation[1], 0f, 100f,
2069                   AlignmentAnnotation.BAR_GRAPH);
2070           initConsensus(complementConsensus);
2071           return true;
2072         }
2073       }
2074     }
2075     return false;
2076   }
2077
2078   private void initConsensus(AlignmentAnnotation aa)
2079   {
2080     aa.hasText = true;
2081     aa.autoCalculated = true;
2082
2083     if (showConsensus)
2084     {
2085       alignment.addAnnotation(aa);
2086     }
2087   }
2088
2089   // these should be extracted from the view model - style and settings for
2090   // derived annotation
2091   private void initOccupancy()
2092   {
2093     if (showOccupancy)
2094     {
2095       occupancy = new AlignmentAnnotation("Occupancy",
2096               MessageManager.getString("label.occupancy_descr"),
2097               new Annotation[1], 0f, alignment.getHeight(),
2098               AlignmentAnnotation.BAR_GRAPH);
2099       occupancy.hasText = true;
2100       occupancy.autoCalculated = true;
2101       occupancy.scaleColLabel = true;
2102       occupancy.graph = AlignmentAnnotation.BAR_GRAPH;
2103
2104       alignment.addAnnotation(occupancy);
2105     }
2106   }
2107
2108   private void initConservation()
2109   {
2110     if (showConservation)
2111     {
2112       if (conservation == null)
2113       {
2114         conservation = new AlignmentAnnotation("Conservation",
2115                 MessageManager.formatMessage("label.conservation_descr",
2116                         getConsPercGaps()),
2117                 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
2118         conservation.hasText = true;
2119         conservation.autoCalculated = true;
2120         alignment.addAnnotation(conservation);
2121       }
2122     }
2123   }
2124
2125   private void initQuality()
2126   {
2127     if (showQuality)
2128     {
2129       if (quality == null)
2130       {
2131         quality = new AlignmentAnnotation("Quality",
2132                 MessageManager.getString("label.quality_descr"),
2133                 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
2134         quality.hasText = true;
2135         quality.autoCalculated = true;
2136         alignment.addAnnotation(quality);
2137       }
2138     }
2139   }
2140
2141   private void initRNAStructure()
2142   {
2143     if (alignment.hasRNAStructure() && strucConsensus == null)
2144     {
2145       strucConsensus = new AlignmentAnnotation("StrucConsensus",
2146               MessageManager.getString("label.strucconsensus_descr"),
2147               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
2148       strucConsensus.hasText = true;
2149       strucConsensus.autoCalculated = true;
2150
2151       if (showConsensus)
2152       {
2153         alignment.addAnnotation(strucConsensus);
2154       }
2155     }
2156   }
2157
2158   /*
2159    * (non-Javadoc)
2160    * 
2161    * @see jalview.api.AlignViewportI#calcPanelHeight()
2162    */
2163   @Override
2164   public int calcPanelHeight()
2165   {
2166     // setHeight of panels
2167     AlignmentAnnotation[] anns = getAlignment().getAlignmentAnnotation();
2168     int height = 0;
2169     int charHeight = getCharHeight();
2170     if (anns != null)
2171     {
2172       BitSet graphgrp = new BitSet();
2173       for (AlignmentAnnotation aa : anns)
2174       {
2175         if (aa == null)
2176         {
2177           System.err.println("Null annotation row: ignoring.");
2178           continue;
2179         }
2180         if (!aa.visible)
2181         {
2182           continue;
2183         }
2184         if (aa.graphGroup > -1)
2185         {
2186           if (graphgrp.get(aa.graphGroup))
2187           {
2188             continue;
2189           }
2190           else
2191           {
2192             graphgrp.set(aa.graphGroup);
2193           }
2194         }
2195         aa.height = 0;
2196
2197         if (aa.hasText)
2198         {
2199           aa.height += charHeight;
2200         }
2201
2202         if (aa.hasIcons)
2203         {
2204           aa.height += 16;
2205         }
2206
2207         if (aa.graph > 0)
2208         {
2209           aa.height += aa.graphHeight;
2210         }
2211
2212         if (aa.height == 0)
2213         {
2214           aa.height = 20;
2215         }
2216
2217         height += aa.height;
2218       }
2219     }
2220     if (height == 0)
2221     {
2222       // set minimum
2223       height = 20;
2224     }
2225     return height;
2226   }
2227
2228   @Override
2229   public void updateGroupAnnotationSettings(boolean applyGlobalSettings,
2230           boolean preserveNewGroupSettings)
2231   {
2232     boolean updateCalcs = false;
2233     boolean conv = isShowGroupConservation();
2234     boolean cons = isShowGroupConsensus();
2235     boolean showprf = isShowSequenceLogo();
2236     boolean showConsHist = isShowConsensusHistogram();
2237     boolean normLogo = isNormaliseSequenceLogo();
2238     boolean showHMMPrf = isShowHMMSequenceLogo();
2239     boolean showInfoHist = isShowInformationHistogram();
2240     boolean normHMMLogo = isNormaliseHMMSequenceLogo();
2241
2242     /**
2243      * TODO reorder the annotation rows according to group/sequence ordering on
2244      * alignment
2245      */
2246     boolean sortg = true;
2247
2248     // remove old automatic annotation
2249     // add any new annotation
2250
2251     // intersect alignment annotation with alignment groups
2252
2253     AlignmentAnnotation[] aan = alignment.getAlignmentAnnotation();
2254     List<SequenceGroup> oldrfs = new ArrayList<>();
2255     if (aan != null)
2256     {
2257       for (int an = 0; an < aan.length; an++)
2258       {
2259         if (aan[an].autoCalculated && aan[an].groupRef != null)
2260         {
2261           oldrfs.add(aan[an].groupRef);
2262           alignment.deleteAnnotation(aan[an], false);
2263         }
2264       }
2265     }
2266     if (alignment.getGroups() != null)
2267     {
2268       for (SequenceGroup sg : alignment.getGroups())
2269       {
2270         updateCalcs = false;
2271         if (applyGlobalSettings
2272                 || (!preserveNewGroupSettings && !oldrfs.contains(sg)))
2273         {
2274           // set defaults for this group's conservation/consensus
2275           sg.setshowSequenceLogo(showprf);
2276           sg.setShowConsensusHistogram(showConsHist);
2277           sg.setNormaliseSequenceLogo(normLogo);
2278           sg.setShowHMMSequenceLogo(showHMMPrf);
2279           sg.setShowInformationHistogram(showInfoHist);
2280           sg.setNormaliseHMMSequenceLogo(normHMMLogo);
2281         }
2282         if (conv)
2283         {
2284           updateCalcs = true;
2285           alignment.addAnnotation(sg.getConservationRow(), 0);
2286         }
2287         if (cons)
2288         {
2289           updateCalcs = true;
2290           alignment.addAnnotation(sg.getConsensus(), 0);
2291         }
2292         // refresh the annotation rows
2293         if (updateCalcs)
2294         {
2295           sg.recalcConservation();
2296         }
2297       }
2298     }
2299     oldrfs.clear();
2300   }
2301
2302   @Override
2303   public boolean isDisplayReferenceSeq()
2304   {
2305     return alignment.hasSeqrep() && viewStyle.isDisplayReferenceSeq();
2306   }
2307
2308   @Override
2309   public void setDisplayReferenceSeq(boolean displayReferenceSeq)
2310   {
2311     viewStyle.setDisplayReferenceSeq(displayReferenceSeq);
2312   }
2313
2314   @Override
2315   public boolean isColourByReferenceSeq()
2316   {
2317     return alignment.hasSeqrep() && viewStyle.isColourByReferenceSeq();
2318   }
2319
2320   @Override
2321   public Color getSequenceColour(SequenceI seq)
2322   {
2323     Color sqc = sequenceColours.get(seq);
2324     return (sqc == null ? Color.white : sqc);
2325   }
2326
2327   @Override
2328   public void setSequenceColour(SequenceI seq, Color col)
2329   {
2330     if (col == null)
2331     {
2332       sequenceColours.remove(seq);
2333     }
2334     else
2335     {
2336       sequenceColours.put(seq, col);
2337     }
2338   }
2339
2340   @Override
2341   public void updateSequenceIdColours()
2342   {
2343     for (SequenceGroup sg : alignment.getGroups())
2344     {
2345       if (sg.idColour != null)
2346       {
2347         for (SequenceI s : sg.getSequences(getHiddenRepSequences()))
2348         {
2349           sequenceColours.put(s, sg.idColour);
2350         }
2351       }
2352     }
2353   }
2354
2355   @Override
2356   public void clearSequenceColours()
2357   {
2358     sequenceColours.clear();
2359   };
2360
2361   @Override
2362   public AlignViewportI getCodingComplement()
2363   {
2364     return this.codingComplement;
2365   }
2366
2367   /**
2368    * Set this as the (cDna/protein) complement of the given viewport. Also
2369    * ensures the reverse relationship is set on the given viewport.
2370    */
2371   @Override
2372   public void setCodingComplement(AlignViewportI av)
2373   {
2374     if (this == av)
2375     {
2376       System.err.println("Ignoring recursive setCodingComplement request");
2377     }
2378     else
2379     {
2380       this.codingComplement = av;
2381       // avoid infinite recursion!
2382       if (av.getCodingComplement() != this)
2383       {
2384         av.setCodingComplement(this);
2385       }
2386     }
2387   }
2388
2389   @Override
2390   public boolean isNucleotide()
2391   {
2392     return getAlignment() == null ? false : getAlignment().isNucleotide();
2393   }
2394
2395   @Override
2396   public FeaturesDisplayedI getFeaturesDisplayed()
2397   {
2398     return featuresDisplayed;
2399   }
2400
2401   @Override
2402   public void setFeaturesDisplayed(FeaturesDisplayedI featuresDisplayedI)
2403   {
2404     featuresDisplayed = featuresDisplayedI;
2405   }
2406
2407   @Override
2408   public boolean areFeaturesDisplayed()
2409   {
2410     return featuresDisplayed != null
2411             && featuresDisplayed.getRegisteredFeaturesCount() > 0;
2412   }
2413
2414   /**
2415    * set the flag
2416    * 
2417    * @param b
2418    *          features are displayed if true
2419    */
2420   @Override
2421   public void setShowSequenceFeatures(boolean b)
2422   {
2423     viewStyle.setShowSequenceFeatures(b);
2424   }
2425
2426   @Override
2427   public boolean isShowSequenceFeatures()
2428   {
2429     return viewStyle.isShowSequenceFeatures();
2430   }
2431
2432   @Override
2433   public void setShowSequenceFeaturesHeight(boolean selected)
2434   {
2435     viewStyle.setShowSequenceFeaturesHeight(selected);
2436   }
2437
2438   @Override
2439   public boolean isShowSequenceFeaturesHeight()
2440   {
2441     return viewStyle.isShowSequenceFeaturesHeight();
2442   }
2443
2444   @Override
2445   public void setShowAnnotation(boolean b)
2446   {
2447     viewStyle.setShowAnnotation(b);
2448   }
2449
2450   @Override
2451   public boolean isShowAnnotation()
2452   {
2453     return viewStyle.isShowAnnotation();
2454   }
2455
2456   @Override
2457   public boolean isRightAlignIds()
2458   {
2459     return viewStyle.isRightAlignIds();
2460   }
2461
2462   @Override
2463   public void setRightAlignIds(boolean rightAlignIds)
2464   {
2465     viewStyle.setRightAlignIds(rightAlignIds);
2466   }
2467
2468   @Override
2469   public boolean getConservationSelected()
2470   {
2471     return viewStyle.getConservationSelected();
2472   }
2473
2474   @Override
2475   public void setShowBoxes(boolean state)
2476   {
2477     viewStyle.setShowBoxes(state);
2478   }
2479
2480   /**
2481    * @return
2482    * @see jalview.api.ViewStyleI#getTextColour()
2483    */
2484   @Override
2485   public Color getTextColour()
2486   {
2487     return viewStyle.getTextColour();
2488   }
2489
2490   /**
2491    * @return
2492    * @see jalview.api.ViewStyleI#getTextColour2()
2493    */
2494   @Override
2495   public Color getTextColour2()
2496   {
2497     return viewStyle.getTextColour2();
2498   }
2499
2500   /**
2501    * @return
2502    * @see jalview.api.ViewStyleI#getThresholdTextColour()
2503    */
2504   @Override
2505   public int getThresholdTextColour()
2506   {
2507     return viewStyle.getThresholdTextColour();
2508   }
2509
2510   /**
2511    * @return
2512    * @see jalview.api.ViewStyleI#isConservationColourSelected()
2513    */
2514   @Override
2515   public boolean isConservationColourSelected()
2516   {
2517     return viewStyle.isConservationColourSelected();
2518   }
2519
2520   /**
2521    * @return
2522    * @see jalview.api.ViewStyleI#isRenderGaps()
2523    */
2524   @Override
2525   public boolean isRenderGaps()
2526   {
2527     return viewStyle.isRenderGaps();
2528   }
2529
2530   /**
2531    * @return
2532    * @see jalview.api.ViewStyleI#isShowColourText()
2533    */
2534   @Override
2535   public boolean isShowColourText()
2536   {
2537     return viewStyle.isShowColourText();
2538   }
2539
2540   /**
2541    * @param conservationColourSelected
2542    * @see jalview.api.ViewStyleI#setConservationColourSelected(boolean)
2543    */
2544   @Override
2545   public void setConservationColourSelected(
2546           boolean conservationColourSelected)
2547   {
2548     viewStyle.setConservationColourSelected(conservationColourSelected);
2549   }
2550
2551   /**
2552    * @param showColourText
2553    * @see jalview.api.ViewStyleI#setShowColourText(boolean)
2554    */
2555   @Override
2556   public void setShowColourText(boolean showColourText)
2557   {
2558     viewStyle.setShowColourText(showColourText);
2559   }
2560
2561   /**
2562    * @param textColour
2563    * @see jalview.api.ViewStyleI#setTextColour(java.awt.Color)
2564    */
2565   @Override
2566   public void setTextColour(Color textColour)
2567   {
2568     viewStyle.setTextColour(textColour);
2569   }
2570
2571   /**
2572    * @param thresholdTextColour
2573    * @see jalview.api.ViewStyleI#setThresholdTextColour(int)
2574    */
2575   @Override
2576   public void setThresholdTextColour(int thresholdTextColour)
2577   {
2578     viewStyle.setThresholdTextColour(thresholdTextColour);
2579   }
2580
2581   /**
2582    * @param textColour2
2583    * @see jalview.api.ViewStyleI#setTextColour2(java.awt.Color)
2584    */
2585   @Override
2586   public void setTextColour2(Color textColour2)
2587   {
2588     viewStyle.setTextColour2(textColour2);
2589   }
2590
2591   @Override
2592   public ViewStyleI getViewStyle()
2593   {
2594     return new ViewStyle(viewStyle);
2595   }
2596
2597   @Override
2598   public void setViewStyle(ViewStyleI settingsForView)
2599   {
2600     viewStyle = new ViewStyle(settingsForView);
2601     if (residueShading != null)
2602     {
2603       residueShading.setConservationApplied(
2604               settingsForView.isConservationColourSelected());
2605     }
2606   }
2607
2608   @Override
2609   public boolean sameStyle(ViewStyleI them)
2610   {
2611     return viewStyle.sameStyle(them);
2612   }
2613
2614   /**
2615    * @return
2616    * @see jalview.api.ViewStyleI#getIdWidth()
2617    */
2618   @Override
2619   public int getIdWidth()
2620   {
2621     return viewStyle.getIdWidth();
2622   }
2623
2624   /**
2625    * @param i
2626    * @see jalview.api.ViewStyleI#setIdWidth(int)
2627    */
2628   @Override
2629   public void setIdWidth(int i)
2630   {
2631     viewStyle.setIdWidth(i);
2632   }
2633
2634   /**
2635    * @return
2636    * @see jalview.api.ViewStyleI#isCentreColumnLabels()
2637    */
2638   @Override
2639   public boolean isCentreColumnLabels()
2640   {
2641     return viewStyle.isCentreColumnLabels();
2642   }
2643
2644   /**
2645    * @param centreColumnLabels
2646    * @see jalview.api.ViewStyleI#setCentreColumnLabels(boolean)
2647    */
2648   @Override
2649   public void setCentreColumnLabels(boolean centreColumnLabels)
2650   {
2651     viewStyle.setCentreColumnLabels(centreColumnLabels);
2652   }
2653
2654   /**
2655    * @param showdbrefs
2656    * @see jalview.api.ViewStyleI#setShowDBRefs(boolean)
2657    */
2658   @Override
2659   public void setShowDBRefs(boolean showdbrefs)
2660   {
2661     viewStyle.setShowDBRefs(showdbrefs);
2662   }
2663
2664   /**
2665    * @return
2666    * @see jalview.api.ViewStyleI#isShowDBRefs()
2667    */
2668   @Override
2669   public boolean isShowDBRefs()
2670   {
2671     return viewStyle.isShowDBRefs();
2672   }
2673
2674   /**
2675    * @return
2676    * @see jalview.api.ViewStyleI#isShowNPFeats()
2677    */
2678   @Override
2679   public boolean isShowNPFeats()
2680   {
2681     return viewStyle.isShowNPFeats();
2682   }
2683
2684   /**
2685    * @param shownpfeats
2686    * @see jalview.api.ViewStyleI#setShowNPFeats(boolean)
2687    */
2688   @Override
2689   public void setShowNPFeats(boolean shownpfeats)
2690   {
2691     viewStyle.setShowNPFeats(shownpfeats);
2692   }
2693
2694   public abstract StructureSelectionManager getStructureSelectionManager();
2695
2696   /**
2697    * Add one command to the command history list.
2698    * 
2699    * @param command
2700    */
2701   public void addToHistoryList(CommandI command)
2702   {
2703     if (this.historyList != null)
2704     {
2705       this.historyList.push(command);
2706       broadcastCommand(command, false);
2707     }
2708   }
2709
2710   protected void broadcastCommand(CommandI command, boolean undo)
2711   {
2712     getStructureSelectionManager().commandPerformed(command, undo,
2713             getVamsasSource());
2714   }
2715
2716   /**
2717    * Add one command to the command redo list.
2718    * 
2719    * @param command
2720    */
2721   public void addToRedoList(CommandI command)
2722   {
2723     if (this.redoList != null)
2724     {
2725       this.redoList.push(command);
2726     }
2727     broadcastCommand(command, true);
2728   }
2729
2730   /**
2731    * Clear the command redo list.
2732    */
2733   public void clearRedoList()
2734   {
2735     if (this.redoList != null)
2736     {
2737       this.redoList.clear();
2738     }
2739   }
2740
2741   public void setHistoryList(Deque<CommandI> list)
2742   {
2743     this.historyList = list;
2744   }
2745
2746   public Deque<CommandI> getHistoryList()
2747   {
2748     return this.historyList;
2749   }
2750
2751   public void setRedoList(Deque<CommandI> list)
2752   {
2753     this.redoList = list;
2754   }
2755
2756   public Deque<CommandI> getRedoList()
2757   {
2758     return this.redoList;
2759   }
2760
2761   @Override
2762   public VamsasSource getVamsasSource()
2763   {
2764     return this;
2765   }
2766
2767   public SequenceAnnotationOrder getSortAnnotationsBy()
2768   {
2769     return sortAnnotationsBy;
2770   }
2771
2772   public void setSortAnnotationsBy(
2773           SequenceAnnotationOrder sortAnnotationsBy)
2774   {
2775     this.sortAnnotationsBy = sortAnnotationsBy;
2776   }
2777
2778   public boolean isShowAutocalculatedAbove()
2779   {
2780     return showAutocalculatedAbove;
2781   }
2782
2783   public void setShowAutocalculatedAbove(boolean showAutocalculatedAbove)
2784   {
2785     this.showAutocalculatedAbove = showAutocalculatedAbove;
2786   }
2787
2788   @Override
2789   public boolean isScaleProteinAsCdna()
2790   {
2791     return viewStyle.isScaleProteinAsCdna();
2792   }
2793
2794   @Override
2795   public void setScaleProteinAsCdna(boolean b)
2796   {
2797     viewStyle.setScaleProteinAsCdna(b);
2798   }
2799
2800   @Override
2801   public boolean isProteinFontAsCdna()
2802   {
2803     return viewStyle.isProteinFontAsCdna();
2804   }
2805
2806   @Override
2807   public void setProteinFontAsCdna(boolean b)
2808   {
2809     viewStyle.setProteinFontAsCdna(b);
2810   }
2811
2812   /**
2813    * @return true if view should scroll to show the highlighted region of a
2814    *         sequence
2815    * @return
2816    */
2817   @Override
2818   public final boolean isFollowHighlight()
2819   {
2820     return followHighlight;
2821   }
2822
2823   @Override
2824   public final void setFollowHighlight(boolean b)
2825   {
2826     this.followHighlight = b;
2827   }
2828
2829   @Override
2830   public ViewportRanges getRanges()
2831   {
2832     return ranges;
2833   }
2834
2835   /**
2836    * Helper method to populate the SearchResults with the location in the
2837    * complementary alignment to scroll to, in order to match this one.
2838    * 
2839    * @param sr
2840    *          the SearchResults to add to
2841    * @return the offset (below top of visible region) of the matched sequence
2842    */
2843   protected int findComplementScrollTarget(SearchResultsI sr)
2844   {
2845     final AlignViewportI complement = getCodingComplement();
2846     if (complement == null || !complement.isFollowHighlight())
2847     {
2848       return 0;
2849     }
2850     boolean iAmProtein = !getAlignment().isNucleotide();
2851     AlignmentI proteinAlignment = iAmProtein ? getAlignment()
2852             : complement.getAlignment();
2853     if (proteinAlignment == null)
2854     {
2855       return 0;
2856     }
2857     final List<AlignedCodonFrame> mappings = proteinAlignment
2858             .getCodonFrames();
2859
2860     /*
2861      * Heuristic: find the first mapped sequence (if any) with a non-gapped
2862      * residue in the middle column of the visible region. Scroll the
2863      * complementary alignment to line up the corresponding residue.
2864      */
2865     int seqOffset = 0;
2866     SequenceI sequence = null;
2867
2868     /*
2869      * locate 'middle' column (true middle if an odd number visible, left of
2870      * middle if an even number visible)
2871      */
2872     int middleColumn = ranges.getStartRes()
2873             + (ranges.getEndRes() - ranges.getStartRes()) / 2;
2874     final HiddenSequences hiddenSequences = getAlignment()
2875             .getHiddenSequences();
2876
2877     /*
2878      * searching to the bottom of the alignment gives smoother scrolling across
2879      * all gapped visible regions
2880      */
2881     int lastSeq = alignment.getHeight() - 1;
2882     List<AlignedCodonFrame> seqMappings = null;
2883     for (int seqNo = ranges
2884             .getStartSeq(); seqNo <= lastSeq; seqNo++, seqOffset++)
2885     {
2886       sequence = getAlignment().getSequenceAt(seqNo);
2887       if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
2888       {
2889         continue;
2890       }
2891       if (Comparison.isGap(sequence.getCharAt(middleColumn)))
2892       {
2893         continue;
2894       }
2895       seqMappings = MappingUtils.findMappingsForSequenceAndOthers(sequence,
2896               mappings,
2897               getCodingComplement().getAlignment().getSequences());
2898       if (!seqMappings.isEmpty())
2899       {
2900         break;
2901       }
2902     }
2903
2904     if (sequence == null || seqMappings == null || seqMappings.isEmpty())
2905     {
2906       /*
2907        * No ungapped mapped sequence in middle column - do nothing
2908        */
2909       return 0;
2910     }
2911     MappingUtils.addSearchResults(sr, sequence,
2912             sequence.findPosition(middleColumn), seqMappings);
2913     return seqOffset;
2914   }
2915
2916   /**
2917    * synthesize a column selection if none exists so it covers the given
2918    * selection group. if wholewidth is false, no column selection is made if the
2919    * selection group covers the whole alignment width.
2920    * 
2921    * @param sg
2922    * @param wholewidth
2923    */
2924   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
2925   {
2926     int sgs, sge;
2927     if (sg != null && (sgs = sg.getStartRes()) >= 0
2928             && sg.getStartRes() <= (sge = sg.getEndRes())
2929             && !this.hasSelectedColumns())
2930     {
2931       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
2932       {
2933         // do nothing
2934         return;
2935       }
2936       if (colSel == null)
2937       {
2938         colSel = new ColumnSelection();
2939       }
2940       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
2941       {
2942         colSel.addElement(cspos);
2943       }
2944     }
2945   }
2946
2947   /**
2948    * hold status of current selection group - defined on alignment or not.
2949    */
2950   private boolean selectionIsDefinedGroup = false;
2951
2952   @Override
2953   public boolean isSelectionDefinedGroup()
2954   {
2955     if (selectionGroup == null)
2956     {
2957       return false;
2958     }
2959     if (isSelectionGroupChanged(true))
2960     {
2961       selectionIsDefinedGroup = false;
2962       List<SequenceGroup> gps = alignment.getGroups();
2963       if (gps == null || gps.size() == 0)
2964       {
2965         selectionIsDefinedGroup = false;
2966       }
2967       else
2968       {
2969         selectionIsDefinedGroup = gps.contains(selectionGroup);
2970       }
2971     }
2972     return selectionGroup.isDefined() || selectionIsDefinedGroup;
2973   }
2974
2975   /**
2976    * null, or currently highlighted results on this view
2977    */
2978   private SearchResultsI searchResults = null;
2979
2980   protected TreeModel currentTree = null;
2981
2982   @Override
2983   public boolean hasSearchResults()
2984   {
2985     return searchResults != null;
2986   }
2987
2988   @Override
2989   public void setSearchResults(SearchResultsI results)
2990   {
2991     searchResults = results;
2992   }
2993
2994   @Override
2995   public SearchResultsI getSearchResults()
2996   {
2997     return searchResults;
2998   }
2999
3000   /**
3001    * get the consensus sequence as displayed under the PID consensus annotation
3002    * row.
3003    * 
3004    * @return consensus sequence as a new sequence object
3005    */
3006   public SequenceI getConsensusSeq()
3007   {
3008     if (consensus == null)
3009     {
3010       updateConsensus(null);
3011     }
3012     if (consensus == null)
3013     {
3014       return null;
3015     }
3016     StringBuffer seqs = new StringBuffer();
3017     for (int i = 0; i < consensus.annotations.length; i++)
3018     {
3019       Annotation annotation = consensus.annotations[i];
3020       if (annotation != null)
3021       {
3022         String description = annotation.description;
3023         if (description != null && description.startsWith("["))
3024         {
3025           // consensus is a tie - just pick the first one
3026           seqs.append(description.charAt(1));
3027         }
3028         else
3029         {
3030           seqs.append(annotation.displayCharacter);
3031         }
3032       }
3033     }
3034
3035     SequenceI sq = new Sequence("Consensus", seqs.toString());
3036     sq.setDescription("Percentage Identity Consensus "
3037             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
3038     return sq;
3039   }
3040
3041   public boolean hasReferenceAnnotation()
3042   {
3043     AlignmentAnnotation[] annots = this.alignment.getAlignmentAnnotation();
3044     for (AlignmentAnnotation annot : annots)
3045     {
3046       if ("RF".equals(annot.label) || annot.label.contains("Reference"))
3047       {
3048         return true;
3049       }
3050     }
3051     return false;
3052   }
3053
3054   @Override
3055   public void setCurrentTree(TreeModel tree)
3056   {
3057     currentTree = tree;
3058   }
3059
3060   @Override
3061   public TreeModel getCurrentTree()
3062   {
3063     return currentTree;
3064   }
3065
3066   @Override
3067   public boolean isNormaliseSequenceLogo()
3068   {
3069     return normaliseSequenceLogo;
3070   }
3071
3072   public void setNormaliseSequenceLogo(boolean state)
3073   {
3074     normaliseSequenceLogo = state;
3075   }
3076
3077   @Override
3078   public boolean isNormaliseHMMSequenceLogo()
3079   {
3080     return hmmNormaliseSequenceLogo;
3081   }
3082
3083   public void setNormaliseHMMSequenceLogo(boolean state)
3084   {
3085     hmmNormaliseSequenceLogo = state;
3086   }
3087 }