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