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