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