18c2aed5ccb2db613bdf425967e1a97d79e4207a
[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     }
1764
1765     return sequences;
1766   }
1767
1768   @Override
1769   public SequenceI[] getSequenceSelection()
1770   {
1771     SequenceI[] sequences = null;
1772     if (selectionGroup != null)
1773     {
1774       sequences = selectionGroup.getSequencesInOrder(alignment);
1775     }
1776     if (sequences == null)
1777     {
1778       sequences = alignment.getSequencesArray();
1779     }
1780     return sequences;
1781   }
1782
1783   @Override
1784   public jalview.datamodel.AlignmentView getAlignmentView(
1785           boolean selectedOnly)
1786   {
1787     return getAlignmentView(selectedOnly, false);
1788   }
1789
1790   @Override
1791   public jalview.datamodel.AlignmentView getAlignmentView(
1792           boolean selectedOnly, boolean markGroups)
1793   {
1794     return new AlignmentView(alignment, alignment.getHiddenColumns(),
1795             selectionGroup,
1796             alignment.getHiddenColumns() != null
1797                     && alignment.getHiddenColumns().hasHiddenColumns(),
1798             selectedOnly, markGroups);
1799   }
1800
1801   @Override
1802   public String[] getViewAsString(boolean selectedRegionOnly)
1803   {
1804     return getViewAsString(selectedRegionOnly, true);
1805   }
1806
1807   @Override
1808   public String[] getViewAsString(boolean selectedRegionOnly,
1809           boolean exportHiddenSeqs)
1810   {
1811     String[] selection = null;
1812     SequenceI[] seqs = null;
1813     int i, iSize;
1814     int start = 0, end = 0;
1815     if (selectedRegionOnly && selectionGroup != null)
1816     {
1817       iSize = selectionGroup.getSize();
1818       seqs = selectionGroup.getSequencesInOrder(alignment);
1819       start = selectionGroup.getStartRes();
1820       end = selectionGroup.getEndRes() + 1;
1821     }
1822     else
1823     {
1824       if (hasHiddenRows() && exportHiddenSeqs)
1825       {
1826         AlignmentI fullAlignment = alignment.getHiddenSequences()
1827                 .getFullAlignment();
1828         iSize = fullAlignment.getHeight();
1829         seqs = fullAlignment.getSequencesArray();
1830         end = fullAlignment.getWidth();
1831       }
1832       else
1833       {
1834         iSize = alignment.getHeight();
1835         seqs = alignment.getSequencesArray();
1836         end = alignment.getWidth();
1837       }
1838     }
1839
1840     selection = new String[iSize];
1841     if (alignment.getHiddenColumns() != null
1842             && alignment.getHiddenColumns().hasHiddenColumns())
1843     {
1844       for (i = 0; i < iSize; i++)
1845       {
1846         Iterator<int[]> blocks = alignment.getHiddenColumns()
1847                 .getVisContigsIterator(start, end + 1, false);
1848         selection[i] = seqs[i].getSequenceStringFromIterator(blocks);
1849       }
1850     }
1851     else
1852     {
1853       for (i = 0; i < iSize; i++)
1854       {
1855         selection[i] = seqs[i].getSequenceAsString(start, end);
1856       }
1857
1858     }
1859     return selection;
1860   }
1861
1862   @Override
1863   public List<int[]> getVisibleRegionBoundaries(int min, int max)
1864   {
1865     ArrayList<int[]> regions = new ArrayList<>();
1866     int start = min;
1867     int end = max;
1868
1869     do
1870     {
1871       HiddenColumns hidden = alignment.getHiddenColumns();
1872       if (hidden != null && hidden.hasHiddenColumns())
1873       {
1874         if (start == 0)
1875         {
1876           start = hidden.visibleToAbsoluteColumn(start);
1877         }
1878
1879         end = hidden.getNextHiddenBoundary(false, start);
1880         if (start == end)
1881         {
1882           end = max;
1883         }
1884         if (end > max)
1885         {
1886           end = max;
1887         }
1888       }
1889
1890       regions.add(new int[] { start, end });
1891
1892       if (hidden != null && hidden.hasHiddenColumns())
1893       {
1894         start = hidden.visibleToAbsoluteColumn(end);
1895         start = hidden.getNextHiddenBoundary(true, start) + 1;
1896       }
1897     } while (end < max);
1898
1899     int[][] startEnd = new int[regions.size()][2];
1900
1901     return regions;
1902   }
1903
1904   @Override
1905   public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(
1906           boolean selectedOnly)
1907   {
1908     ArrayList<AlignmentAnnotation> ala = new ArrayList<>();
1909     AlignmentAnnotation[] aa;
1910     if ((aa = alignment.getAlignmentAnnotation()) != null)
1911     {
1912       for (AlignmentAnnotation annot : aa)
1913       {
1914         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
1915         if (selectedOnly && selectionGroup != null)
1916         {
1917           clone.makeVisibleAnnotation(
1918                   selectionGroup.getStartRes(), selectionGroup.getEndRes(),
1919                   alignment.getHiddenColumns());
1920         }
1921         else
1922         {
1923           clone.makeVisibleAnnotation(alignment.getHiddenColumns());
1924         }
1925         ala.add(clone);
1926       }
1927     }
1928     return ala;
1929   }
1930
1931   @Override
1932   public boolean isPadGaps()
1933   {
1934     return padGaps;
1935   }
1936
1937   @Override
1938   public void setPadGaps(boolean padGaps)
1939   {
1940     this.padGaps = padGaps;
1941   }
1942
1943   /**
1944    * apply any post-edit constraints and trigger any calculations needed after
1945    * an edit has been performed on the alignment
1946    * 
1947    * @param ap
1948    */
1949   @Override
1950   public void alignmentChanged(AlignmentViewPanel ap)
1951   {
1952     if (isPadGaps())
1953     {
1954       alignment.padGaps();
1955     }
1956
1957     // Reset endRes of groups if beyond alignment width
1958     int alWidth = alignment.getWidth();
1959     List<SequenceGroup> groups = alignment.getGroups();
1960     if (groups != null)
1961     {
1962       for (SequenceGroup sg : groups)
1963       {
1964         if (sg.getEndRes() > alWidth)
1965         {
1966           sg.setEndRes(alWidth - 1);
1967         }
1968       }
1969     }
1970
1971     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1972     {
1973       selectionGroup.setEndRes(alWidth - 1);
1974     }
1975
1976     updateAllColourSchemes();
1977     calculator.restartWorkers();
1978   }
1979
1980   /**
1981    * reset scope and do calculations for all applied colourschemes on alignment
1982    */
1983   void updateAllColourSchemes()
1984   {
1985     ResidueShaderI rs = residueShading;
1986     if (rs != null)
1987     {
1988       rs.alignmentChanged(alignment, hiddenRepSequences);
1989
1990       rs.setConsensus(consensusProfiles);
1991       if (rs.conservationApplied())
1992       {
1993         rs.setConservation(Conservation.calculateConservation("All",
1994                 alignment.getSequences(), 0, alignment.getWidth(), false,
1995                 getConsPercGaps(), false));
1996       }
1997     }
1998
1999     for (SequenceGroup sg : alignment.getGroups())
2000     {
2001       if (sg.cs != null)
2002       {
2003         sg.cs.alignmentChanged(sg, hiddenRepSequences);
2004       }
2005       sg.recalcConservation();
2006     }
2007   }
2008
2009   protected void initAutoAnnotation()
2010   {
2011     // TODO: add menu option action that nulls or creates consensus object
2012     // depending on if the user wants to see the annotation or not in a
2013     // specific alignment
2014
2015     if (consensusProfiles == null && !isDataset)
2016     {
2017       if (!alignment.isNucleotide())
2018       {
2019         initConservation();
2020         initQuality();
2021       }
2022       else
2023       {
2024         initRNAStructure();
2025       }
2026       consensus = new AlignmentAnnotation("Consensus",
2027               MessageManager.getString("label.consensus_descr"),
2028               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
2029       initConsensus(consensus);
2030
2031       initOccupancy();
2032
2033       initComplementConsensus();
2034     }
2035   }
2036
2037   /**
2038    * If this is a protein alignment and there are mappings to cDNA, adds the
2039    * cDNA consensus annotation and returns true, else returns false.
2040    */
2041   public boolean initComplementConsensus()
2042   {
2043     if (!alignment.isNucleotide())
2044     {
2045       final List<AlignedCodonFrame> codonMappings = alignment
2046               .getCodonFrames();
2047       if (codonMappings != null && !codonMappings.isEmpty())
2048       {
2049         boolean doConsensus = false;
2050         for (AlignedCodonFrame mapping : codonMappings)
2051         {
2052           // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame?
2053           MapList[] mapLists = mapping.getdnaToProt();
2054           // mapLists can be empty if project load has not finished resolving
2055           // seqs
2056           if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
2057           {
2058             doConsensus = true;
2059             break;
2060           }
2061         }
2062         if (doConsensus)
2063         {
2064           complementConsensus = new AlignmentAnnotation("cDNA Consensus",
2065                   MessageManager
2066                           .getString("label.complement_consensus_descr"),
2067                   new Annotation[1], 0f, 100f,
2068                   AlignmentAnnotation.BAR_GRAPH);
2069           initConsensus(complementConsensus);
2070           return true;
2071         }
2072       }
2073     }
2074     return false;
2075   }
2076
2077   private void initConsensus(AlignmentAnnotation aa)
2078   {
2079     aa.hasText = true;
2080     aa.autoCalculated = true;
2081
2082     if (showConsensus)
2083     {
2084       alignment.addAnnotation(aa);
2085     }
2086   }
2087
2088   // these should be extracted from the view model - style and settings for
2089   // derived annotation
2090   private void initOccupancy()
2091   {
2092     if (showOccupancy)
2093     {
2094       occupancy = new AlignmentAnnotation("Occupancy",
2095               MessageManager.getString("label.occupancy_descr"),
2096               new Annotation[1], 0f, alignment.getHeight(),
2097               AlignmentAnnotation.BAR_GRAPH);
2098       occupancy.hasText = true;
2099       occupancy.autoCalculated = true;
2100       occupancy.scaleColLabel = true;
2101       occupancy.graph = AlignmentAnnotation.BAR_GRAPH;
2102
2103       alignment.addAnnotation(occupancy);
2104     }
2105   }
2106
2107   private void initConservation()
2108   {
2109     if (showConservation)
2110     {
2111       if (conservation == null)
2112       {
2113         conservation = new AlignmentAnnotation("Conservation",
2114                 MessageManager.formatMessage("label.conservation_descr",
2115                         getConsPercGaps()),
2116                 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
2117         conservation.hasText = true;
2118         conservation.autoCalculated = true;
2119         alignment.addAnnotation(conservation);
2120       }
2121     }
2122   }
2123
2124   private void initQuality()
2125   {
2126     if (showQuality)
2127     {
2128       if (quality == null)
2129       {
2130         quality = new AlignmentAnnotation("Quality",
2131                 MessageManager.getString("label.quality_descr"),
2132                 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
2133         quality.hasText = true;
2134         quality.autoCalculated = true;
2135         alignment.addAnnotation(quality);
2136       }
2137     }
2138   }
2139
2140   private void initRNAStructure()
2141   {
2142     if (alignment.hasRNAStructure() && strucConsensus == null)
2143     {
2144       strucConsensus = new AlignmentAnnotation("StrucConsensus",
2145               MessageManager.getString("label.strucconsensus_descr"),
2146               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
2147       strucConsensus.hasText = true;
2148       strucConsensus.autoCalculated = true;
2149
2150       if (showConsensus)
2151       {
2152         alignment.addAnnotation(strucConsensus);
2153       }
2154     }
2155   }
2156
2157   /*
2158    * (non-Javadoc)
2159    * 
2160    * @see jalview.api.AlignViewportI#calcPanelHeight()
2161    */
2162   @Override
2163   public int calcPanelHeight()
2164   {
2165     // setHeight of panels
2166     AlignmentAnnotation[] anns = getAlignment().getAlignmentAnnotation();
2167     int height = 0;
2168     int charHeight = getCharHeight();
2169     if (anns != null)
2170     {
2171       BitSet graphgrp = new BitSet();
2172       for (AlignmentAnnotation aa : anns)
2173       {
2174         if (aa == null)
2175         {
2176           System.err.println("Null annotation row: ignoring.");
2177           continue;
2178         }
2179         if (!aa.visible)
2180         {
2181           continue;
2182         }
2183         if (aa.graphGroup > -1)
2184         {
2185           if (graphgrp.get(aa.graphGroup))
2186           {
2187             continue;
2188           }
2189           else
2190           {
2191             graphgrp.set(aa.graphGroup);
2192           }
2193         }
2194         aa.height = 0;
2195
2196         if (aa.hasText)
2197         {
2198           aa.height += charHeight;
2199         }
2200
2201         if (aa.hasIcons)
2202         {
2203           aa.height += 16;
2204         }
2205
2206         if (aa.graph > 0)
2207         {
2208           aa.height += aa.graphHeight;
2209         }
2210
2211         if (aa.height == 0)
2212         {
2213           aa.height = 20;
2214         }
2215
2216         height += aa.height;
2217       }
2218     }
2219     if (height == 0)
2220     {
2221       // set minimum
2222       height = 20;
2223     }
2224     return height;
2225   }
2226
2227   @Override
2228   public void updateGroupAnnotationSettings(boolean applyGlobalSettings,
2229           boolean preserveNewGroupSettings)
2230   {
2231     boolean updateCalcs = false;
2232     boolean conv = isShowGroupConservation();
2233     boolean cons = isShowGroupConsensus();
2234     boolean showprf = isShowSequenceLogo();
2235     boolean showConsHist = isShowConsensusHistogram();
2236     boolean normLogo = isNormaliseSequenceLogo();
2237     boolean showHMMPrf = isShowHMMSequenceLogo();
2238     boolean showInfoHist = isShowInformationHistogram();
2239     boolean normHMMLogo = isNormaliseHMMSequenceLogo();
2240
2241     /**
2242      * TODO reorder the annotation rows according to group/sequence ordering on
2243      * alignment
2244      */
2245     boolean sortg = true;
2246
2247     // remove old automatic annotation
2248     // add any new annotation
2249
2250     // intersect alignment annotation with alignment groups
2251
2252     AlignmentAnnotation[] aan = alignment.getAlignmentAnnotation();
2253     List<SequenceGroup> oldrfs = new ArrayList<>();
2254     if (aan != null)
2255     {
2256       for (int an = 0; an < aan.length; an++)
2257       {
2258         if (aan[an].autoCalculated && aan[an].groupRef != null)
2259         {
2260           oldrfs.add(aan[an].groupRef);
2261           alignment.deleteAnnotation(aan[an], false);
2262         }
2263       }
2264     }
2265     if (alignment.getGroups() != null)
2266     {
2267       for (SequenceGroup sg : alignment.getGroups())
2268       {
2269         updateCalcs = false;
2270         if (applyGlobalSettings
2271                 || (!preserveNewGroupSettings && !oldrfs.contains(sg)))
2272         {
2273           // set defaults for this group's conservation/consensus
2274           sg.setshowSequenceLogo(showprf);
2275           sg.setShowConsensusHistogram(showConsHist);
2276           sg.setNormaliseSequenceLogo(normLogo);
2277           sg.setShowHMMSequenceLogo(showHMMPrf);
2278           sg.setShowInformationHistogram(showInfoHist);
2279           sg.setNormaliseHMMSequenceLogo(normHMMLogo);
2280         }
2281         if (conv)
2282         {
2283           updateCalcs = true;
2284           alignment.addAnnotation(sg.getConservationRow(), 0);
2285         }
2286         if (cons)
2287         {
2288           updateCalcs = true;
2289           alignment.addAnnotation(sg.getConsensus(), 0);
2290         }
2291         // refresh the annotation rows
2292         if (updateCalcs)
2293         {
2294           sg.recalcConservation();
2295         }
2296       }
2297     }
2298     oldrfs.clear();
2299   }
2300
2301   @Override
2302   public boolean isDisplayReferenceSeq()
2303   {
2304     return alignment.hasSeqrep() && viewStyle.isDisplayReferenceSeq();
2305   }
2306
2307   @Override
2308   public void setDisplayReferenceSeq(boolean displayReferenceSeq)
2309   {
2310     viewStyle.setDisplayReferenceSeq(displayReferenceSeq);
2311   }
2312
2313   @Override
2314   public boolean isColourByReferenceSeq()
2315   {
2316     return alignment.hasSeqrep() && viewStyle.isColourByReferenceSeq();
2317   }
2318
2319   @Override
2320   public Color getSequenceColour(SequenceI seq)
2321   {
2322     Color sqc = sequenceColours.get(seq);
2323     return (sqc == null ? Color.white : sqc);
2324   }
2325
2326   @Override
2327   public void setSequenceColour(SequenceI seq, Color col)
2328   {
2329     if (col == null)
2330     {
2331       sequenceColours.remove(seq);
2332     }
2333     else
2334     {
2335       sequenceColours.put(seq, col);
2336     }
2337   }
2338
2339   @Override
2340   public void updateSequenceIdColours()
2341   {
2342     for (SequenceGroup sg : alignment.getGroups())
2343     {
2344       if (sg.idColour != null)
2345       {
2346         for (SequenceI s : sg.getSequences(getHiddenRepSequences()))
2347         {
2348           sequenceColours.put(s, sg.idColour);
2349         }
2350       }
2351     }
2352   }
2353
2354   @Override
2355   public void clearSequenceColours()
2356   {
2357     sequenceColours.clear();
2358   };
2359
2360   @Override
2361   public AlignViewportI getCodingComplement()
2362   {
2363     return this.codingComplement;
2364   }
2365
2366   /**
2367    * Set this as the (cDna/protein) complement of the given viewport. Also
2368    * ensures the reverse relationship is set on the given viewport.
2369    */
2370   @Override
2371   public void setCodingComplement(AlignViewportI av)
2372   {
2373     if (this == av)
2374     {
2375       System.err.println("Ignoring recursive setCodingComplement request");
2376     }
2377     else
2378     {
2379       this.codingComplement = av;
2380       // avoid infinite recursion!
2381       if (av.getCodingComplement() != this)
2382       {
2383         av.setCodingComplement(this);
2384       }
2385     }
2386   }
2387
2388   @Override
2389   public boolean isNucleotide()
2390   {
2391     return getAlignment() == null ? false : getAlignment().isNucleotide();
2392   }
2393
2394   @Override
2395   public FeaturesDisplayedI getFeaturesDisplayed()
2396   {
2397     return featuresDisplayed;
2398   }
2399
2400   @Override
2401   public void setFeaturesDisplayed(FeaturesDisplayedI featuresDisplayedI)
2402   {
2403     featuresDisplayed = featuresDisplayedI;
2404   }
2405
2406   @Override
2407   public boolean areFeaturesDisplayed()
2408   {
2409     return featuresDisplayed != null
2410             && featuresDisplayed.getRegisteredFeaturesCount() > 0;
2411   }
2412
2413   /**
2414    * set the flag
2415    * 
2416    * @param b
2417    *          features are displayed if true
2418    */
2419   @Override
2420   public void setShowSequenceFeatures(boolean b)
2421   {
2422     viewStyle.setShowSequenceFeatures(b);
2423   }
2424
2425   @Override
2426   public boolean isShowSequenceFeatures()
2427   {
2428     return viewStyle.isShowSequenceFeatures();
2429   }
2430
2431   @Override
2432   public void setShowSequenceFeaturesHeight(boolean selected)
2433   {
2434     viewStyle.setShowSequenceFeaturesHeight(selected);
2435   }
2436
2437   @Override
2438   public boolean isShowSequenceFeaturesHeight()
2439   {
2440     return viewStyle.isShowSequenceFeaturesHeight();
2441   }
2442
2443   @Override
2444   public void setShowAnnotation(boolean b)
2445   {
2446     viewStyle.setShowAnnotation(b);
2447   }
2448
2449   @Override
2450   public boolean isShowAnnotation()
2451   {
2452     return viewStyle.isShowAnnotation();
2453   }
2454
2455   @Override
2456   public boolean isRightAlignIds()
2457   {
2458     return viewStyle.isRightAlignIds();
2459   }
2460
2461   @Override
2462   public void setRightAlignIds(boolean rightAlignIds)
2463   {
2464     viewStyle.setRightAlignIds(rightAlignIds);
2465   }
2466
2467   @Override
2468   public boolean getConservationSelected()
2469   {
2470     return viewStyle.getConservationSelected();
2471   }
2472
2473   @Override
2474   public void setShowBoxes(boolean state)
2475   {
2476     viewStyle.setShowBoxes(state);
2477   }
2478
2479   /**
2480    * @return
2481    * @see jalview.api.ViewStyleI#getTextColour()
2482    */
2483   @Override
2484   public Color getTextColour()
2485   {
2486     return viewStyle.getTextColour();
2487   }
2488
2489   /**
2490    * @return
2491    * @see jalview.api.ViewStyleI#getTextColour2()
2492    */
2493   @Override
2494   public Color getTextColour2()
2495   {
2496     return viewStyle.getTextColour2();
2497   }
2498
2499   /**
2500    * @return
2501    * @see jalview.api.ViewStyleI#getThresholdTextColour()
2502    */
2503   @Override
2504   public int getThresholdTextColour()
2505   {
2506     return viewStyle.getThresholdTextColour();
2507   }
2508
2509   /**
2510    * @return
2511    * @see jalview.api.ViewStyleI#isConservationColourSelected()
2512    */
2513   @Override
2514   public boolean isConservationColourSelected()
2515   {
2516     return viewStyle.isConservationColourSelected();
2517   }
2518
2519   /**
2520    * @return
2521    * @see jalview.api.ViewStyleI#isRenderGaps()
2522    */
2523   @Override
2524   public boolean isRenderGaps()
2525   {
2526     return viewStyle.isRenderGaps();
2527   }
2528
2529   /**
2530    * @return
2531    * @see jalview.api.ViewStyleI#isShowColourText()
2532    */
2533   @Override
2534   public boolean isShowColourText()
2535   {
2536     return viewStyle.isShowColourText();
2537   }
2538
2539   /**
2540    * @param conservationColourSelected
2541    * @see jalview.api.ViewStyleI#setConservationColourSelected(boolean)
2542    */
2543   @Override
2544   public void setConservationColourSelected(
2545           boolean conservationColourSelected)
2546   {
2547     viewStyle.setConservationColourSelected(conservationColourSelected);
2548   }
2549
2550   /**
2551    * @param showColourText
2552    * @see jalview.api.ViewStyleI#setShowColourText(boolean)
2553    */
2554   @Override
2555   public void setShowColourText(boolean showColourText)
2556   {
2557     viewStyle.setShowColourText(showColourText);
2558   }
2559
2560   /**
2561    * @param textColour
2562    * @see jalview.api.ViewStyleI#setTextColour(java.awt.Color)
2563    */
2564   @Override
2565   public void setTextColour(Color textColour)
2566   {
2567     viewStyle.setTextColour(textColour);
2568   }
2569
2570   /**
2571    * @param thresholdTextColour
2572    * @see jalview.api.ViewStyleI#setThresholdTextColour(int)
2573    */
2574   @Override
2575   public void setThresholdTextColour(int thresholdTextColour)
2576   {
2577     viewStyle.setThresholdTextColour(thresholdTextColour);
2578   }
2579
2580   /**
2581    * @param textColour2
2582    * @see jalview.api.ViewStyleI#setTextColour2(java.awt.Color)
2583    */
2584   @Override
2585   public void setTextColour2(Color textColour2)
2586   {
2587     viewStyle.setTextColour2(textColour2);
2588   }
2589
2590   @Override
2591   public ViewStyleI getViewStyle()
2592   {
2593     return new ViewStyle(viewStyle);
2594   }
2595
2596   @Override
2597   public void setViewStyle(ViewStyleI settingsForView)
2598   {
2599     viewStyle = new ViewStyle(settingsForView);
2600     if (residueShading != null)
2601     {
2602       residueShading.setConservationApplied(
2603               settingsForView.isConservationColourSelected());
2604     }
2605   }
2606
2607   @Override
2608   public boolean sameStyle(ViewStyleI them)
2609   {
2610     return viewStyle.sameStyle(them);
2611   }
2612
2613   /**
2614    * @return
2615    * @see jalview.api.ViewStyleI#getIdWidth()
2616    */
2617   @Override
2618   public int getIdWidth()
2619   {
2620     return viewStyle.getIdWidth();
2621   }
2622
2623   /**
2624    * @param i
2625    * @see jalview.api.ViewStyleI#setIdWidth(int)
2626    */
2627   @Override
2628   public void setIdWidth(int i)
2629   {
2630     viewStyle.setIdWidth(i);
2631   }
2632
2633   /**
2634    * @return
2635    * @see jalview.api.ViewStyleI#isCentreColumnLabels()
2636    */
2637   @Override
2638   public boolean isCentreColumnLabels()
2639   {
2640     return viewStyle.isCentreColumnLabels();
2641   }
2642
2643   /**
2644    * @param centreColumnLabels
2645    * @see jalview.api.ViewStyleI#setCentreColumnLabels(boolean)
2646    */
2647   @Override
2648   public void setCentreColumnLabels(boolean centreColumnLabels)
2649   {
2650     viewStyle.setCentreColumnLabels(centreColumnLabels);
2651   }
2652
2653   /**
2654    * @param showdbrefs
2655    * @see jalview.api.ViewStyleI#setShowDBRefs(boolean)
2656    */
2657   @Override
2658   public void setShowDBRefs(boolean showdbrefs)
2659   {
2660     viewStyle.setShowDBRefs(showdbrefs);
2661   }
2662
2663   /**
2664    * @return
2665    * @see jalview.api.ViewStyleI#isShowDBRefs()
2666    */
2667   @Override
2668   public boolean isShowDBRefs()
2669   {
2670     return viewStyle.isShowDBRefs();
2671   }
2672
2673   /**
2674    * @return
2675    * @see jalview.api.ViewStyleI#isShowNPFeats()
2676    */
2677   @Override
2678   public boolean isShowNPFeats()
2679   {
2680     return viewStyle.isShowNPFeats();
2681   }
2682
2683   /**
2684    * @param shownpfeats
2685    * @see jalview.api.ViewStyleI#setShowNPFeats(boolean)
2686    */
2687   @Override
2688   public void setShowNPFeats(boolean shownpfeats)
2689   {
2690     viewStyle.setShowNPFeats(shownpfeats);
2691   }
2692
2693   public abstract StructureSelectionManager getStructureSelectionManager();
2694
2695   /**
2696    * Add one command to the command history list.
2697    * 
2698    * @param command
2699    */
2700   public void addToHistoryList(CommandI command)
2701   {
2702     if (this.historyList != null)
2703     {
2704       this.historyList.push(command);
2705       broadcastCommand(command, false);
2706     }
2707   }
2708
2709   protected void broadcastCommand(CommandI command, boolean undo)
2710   {
2711     getStructureSelectionManager().commandPerformed(command, undo,
2712             getVamsasSource());
2713   }
2714
2715   /**
2716    * Add one command to the command redo list.
2717    * 
2718    * @param command
2719    */
2720   public void addToRedoList(CommandI command)
2721   {
2722     if (this.redoList != null)
2723     {
2724       this.redoList.push(command);
2725     }
2726     broadcastCommand(command, true);
2727   }
2728
2729   /**
2730    * Clear the command redo list.
2731    */
2732   public void clearRedoList()
2733   {
2734     if (this.redoList != null)
2735     {
2736       this.redoList.clear();
2737     }
2738   }
2739
2740   public void setHistoryList(Deque<CommandI> list)
2741   {
2742     this.historyList = list;
2743   }
2744
2745   public Deque<CommandI> getHistoryList()
2746   {
2747     return this.historyList;
2748   }
2749
2750   public void setRedoList(Deque<CommandI> list)
2751   {
2752     this.redoList = list;
2753   }
2754
2755   public Deque<CommandI> getRedoList()
2756   {
2757     return this.redoList;
2758   }
2759
2760   @Override
2761   public VamsasSource getVamsasSource()
2762   {
2763     return this;
2764   }
2765
2766   public SequenceAnnotationOrder getSortAnnotationsBy()
2767   {
2768     return sortAnnotationsBy;
2769   }
2770
2771   public void setSortAnnotationsBy(
2772           SequenceAnnotationOrder sortAnnotationsBy)
2773   {
2774     this.sortAnnotationsBy = sortAnnotationsBy;
2775   }
2776
2777   public boolean isShowAutocalculatedAbove()
2778   {
2779     return showAutocalculatedAbove;
2780   }
2781
2782   public void setShowAutocalculatedAbove(boolean showAutocalculatedAbove)
2783   {
2784     this.showAutocalculatedAbove = showAutocalculatedAbove;
2785   }
2786
2787   @Override
2788   public boolean isScaleProteinAsCdna()
2789   {
2790     return viewStyle.isScaleProteinAsCdna();
2791   }
2792
2793   @Override
2794   public void setScaleProteinAsCdna(boolean b)
2795   {
2796     viewStyle.setScaleProteinAsCdna(b);
2797   }
2798
2799   @Override
2800   public boolean isProteinFontAsCdna()
2801   {
2802     return viewStyle.isProteinFontAsCdna();
2803   }
2804
2805   @Override
2806   public void setProteinFontAsCdna(boolean b)
2807   {
2808     viewStyle.setProteinFontAsCdna(b);
2809   }
2810
2811   /**
2812    * @return true if view should scroll to show the highlighted region of a
2813    *         sequence
2814    * @return
2815    */
2816   @Override
2817   public final boolean isFollowHighlight()
2818   {
2819     return followHighlight;
2820   }
2821
2822   @Override
2823   public final void setFollowHighlight(boolean b)
2824   {
2825     this.followHighlight = b;
2826   }
2827
2828   @Override
2829   public ViewportRanges getRanges()
2830   {
2831     return ranges;
2832   }
2833
2834   /**
2835    * Helper method to populate the SearchResults with the location in the
2836    * complementary alignment to scroll to, in order to match this one.
2837    * 
2838    * @param sr
2839    *          the SearchResults to add to
2840    * @return the offset (below top of visible region) of the matched sequence
2841    */
2842   protected int findComplementScrollTarget(SearchResultsI sr)
2843   {
2844     final AlignViewportI complement = getCodingComplement();
2845     if (complement == null || !complement.isFollowHighlight())
2846     {
2847       return 0;
2848     }
2849     boolean iAmProtein = !getAlignment().isNucleotide();
2850     AlignmentI proteinAlignment = iAmProtein ? getAlignment()
2851             : complement.getAlignment();
2852     if (proteinAlignment == null)
2853     {
2854       return 0;
2855     }
2856     final List<AlignedCodonFrame> mappings = proteinAlignment
2857             .getCodonFrames();
2858
2859     /*
2860      * Heuristic: find the first mapped sequence (if any) with a non-gapped
2861      * residue in the middle column of the visible region. Scroll the
2862      * complementary alignment to line up the corresponding residue.
2863      */
2864     int seqOffset = 0;
2865     SequenceI sequence = null;
2866
2867     /*
2868      * locate 'middle' column (true middle if an odd number visible, left of
2869      * middle if an even number visible)
2870      */
2871     int middleColumn = ranges.getStartRes()
2872             + (ranges.getEndRes() - ranges.getStartRes()) / 2;
2873     final HiddenSequences hiddenSequences = getAlignment()
2874             .getHiddenSequences();
2875
2876     /*
2877      * searching to the bottom of the alignment gives smoother scrolling across
2878      * all gapped visible regions
2879      */
2880     int lastSeq = alignment.getHeight() - 1;
2881     List<AlignedCodonFrame> seqMappings = null;
2882     for (int seqNo = ranges
2883             .getStartSeq(); seqNo <= lastSeq; seqNo++, seqOffset++)
2884     {
2885       sequence = getAlignment().getSequenceAt(seqNo);
2886       if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
2887       {
2888         continue;
2889       }
2890       if (Comparison.isGap(sequence.getCharAt(middleColumn)))
2891       {
2892         continue;
2893       }
2894       seqMappings = MappingUtils.findMappingsForSequenceAndOthers(sequence,
2895               mappings,
2896               getCodingComplement().getAlignment().getSequences());
2897       if (!seqMappings.isEmpty())
2898       {
2899         break;
2900       }
2901     }
2902
2903     if (sequence == null || seqMappings == null || seqMappings.isEmpty())
2904     {
2905       /*
2906        * No ungapped mapped sequence in middle column - do nothing
2907        */
2908       return 0;
2909     }
2910     MappingUtils.addSearchResults(sr, sequence,
2911             sequence.findPosition(middleColumn), seqMappings);
2912     return seqOffset;
2913   }
2914
2915   /**
2916    * synthesize a column selection if none exists so it covers the given
2917    * selection group. if wholewidth is false, no column selection is made if the
2918    * selection group covers the whole alignment width.
2919    * 
2920    * @param sg
2921    * @param wholewidth
2922    */
2923   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
2924   {
2925     int sgs, sge;
2926     if (sg != null && (sgs = sg.getStartRes()) >= 0
2927             && sg.getStartRes() <= (sge = sg.getEndRes())
2928             && !this.hasSelectedColumns())
2929     {
2930       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
2931       {
2932         // do nothing
2933         return;
2934       }
2935       if (colSel == null)
2936       {
2937         colSel = new ColumnSelection();
2938       }
2939       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
2940       {
2941         colSel.addElement(cspos);
2942       }
2943     }
2944   }
2945
2946   /**
2947    * hold status of current selection group - defined on alignment or not.
2948    */
2949   private boolean selectionIsDefinedGroup = false;
2950
2951   @Override
2952   public boolean isSelectionDefinedGroup()
2953   {
2954     if (selectionGroup == null)
2955     {
2956       return false;
2957     }
2958     if (isSelectionGroupChanged(true))
2959     {
2960       selectionIsDefinedGroup = false;
2961       List<SequenceGroup> gps = alignment.getGroups();
2962       if (gps == null || gps.size() == 0)
2963       {
2964         selectionIsDefinedGroup = false;
2965       }
2966       else
2967       {
2968         selectionIsDefinedGroup = gps.contains(selectionGroup);
2969       }
2970     }
2971     return selectionGroup.isDefined() || selectionIsDefinedGroup;
2972   }
2973
2974   /**
2975    * null, or currently highlighted results on this view
2976    */
2977   private SearchResultsI searchResults = null;
2978
2979   protected TreeModel currentTree = null;
2980
2981   @Override
2982   public boolean hasSearchResults()
2983   {
2984     return searchResults != null;
2985   }
2986
2987   @Override
2988   public void setSearchResults(SearchResultsI results)
2989   {
2990     searchResults = results;
2991   }
2992
2993   @Override
2994   public SearchResultsI getSearchResults()
2995   {
2996     return searchResults;
2997   }
2998
2999   /**
3000    * get the consensus sequence as displayed under the PID consensus annotation
3001    * row.
3002    * 
3003    * @return consensus sequence as a new sequence object
3004    */
3005   public SequenceI getConsensusSeq()
3006   {
3007     if (consensus == null)
3008     {
3009       updateConsensus(null);
3010     }
3011     if (consensus == null)
3012     {
3013       return null;
3014     }
3015     StringBuffer seqs = new StringBuffer();
3016     for (int i = 0; i < consensus.annotations.length; i++)
3017     {
3018       Annotation annotation = consensus.annotations[i];
3019       if (annotation != null)
3020       {
3021         String description = annotation.description;
3022         if (description != null && description.startsWith("["))
3023         {
3024           // consensus is a tie - just pick the first one
3025           seqs.append(description.charAt(1));
3026         }
3027         else
3028         {
3029           seqs.append(annotation.displayCharacter);
3030         }
3031       }
3032     }
3033
3034     SequenceI sq = new Sequence("Consensus", seqs.toString());
3035     sq.setDescription("Percentage Identity Consensus "
3036             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
3037     return sq;
3038   }
3039
3040   public boolean hasReferenceAnnotation()
3041   {
3042     AlignmentAnnotation[] annots = this.alignment.getAlignmentAnnotation();
3043     for (AlignmentAnnotation annot : annots)
3044     {
3045       if ("RF".equals(annot.label) || annot.label.contains("Reference"))
3046       {
3047         return true;
3048       }
3049     }
3050     return false;
3051   }
3052
3053   @Override
3054   public void setCurrentTree(TreeModel tree)
3055   {
3056     currentTree = tree;
3057   }
3058
3059   @Override
3060   public TreeModel getCurrentTree()
3061   {
3062     return currentTree;
3063   }
3064
3065   @Override
3066   public boolean isNormaliseSequenceLogo()
3067   {
3068     return normaliseSequenceLogo;
3069   }
3070
3071   public void setNormaliseSequenceLogo(boolean state)
3072   {
3073     normaliseSequenceLogo = state;
3074   }
3075
3076   @Override
3077   public boolean isNormaliseHMMSequenceLogo()
3078   {
3079     return hmmNormaliseSequenceLogo;
3080   }
3081
3082   public void setNormaliseHMMSequenceLogo(boolean state)
3083   {
3084     hmmNormaliseSequenceLogo = state;
3085   }
3086 }