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