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