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