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