JAL-1799 adjusted annotation height calculation
[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
636   @Override
637   public ColourSchemeI getGlobalColourScheme()
638   {
639     return globalColourScheme;
640   }
641
642   protected AlignmentAnnotation consensus;
643
644   protected AlignmentAnnotation complementConsensus;
645
646   protected AlignmentAnnotation strucConsensus;
647
648   protected AlignmentAnnotation conservation;
649
650   protected AlignmentAnnotation quality;
651
652   protected AlignmentAnnotation[] groupConsensus;
653
654   protected AlignmentAnnotation[] groupConservation;
655
656   /**
657    * results of alignment consensus analysis for visible portion of view
658    */
659   protected Hashtable[] hconsensus = null;
660
661   /**
662    * results of cDNA complement consensus visible portion of view
663    */
664   protected Hashtable[] hcomplementConsensus = null;
665
666   /**
667    * results of secondary structure base pair consensus for visible portion of
668    * view
669    */
670   protected Hashtable[] hStrucConsensus = null;
671
672   protected Conservation hconservation = null;
673
674   @Override
675   public void setConservation(Conservation cons)
676   {
677     hconservation = cons;
678   }
679
680   /**
681    * percentage gaps allowed in a column before all amino acid properties should
682    * be considered unconserved
683    */
684   int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
685
686   @Override
687   public int getConsPercGaps()
688   {
689     return ConsPercGaps;
690   }
691
692   @Override
693   public void setSequenceConsensusHash(Hashtable[] hconsensus)
694   {
695     this.hconsensus = hconsensus;
696   }
697
698   @Override
699   public void setComplementConsensusHash(Hashtable[] hconsensus)
700   {
701     this.hcomplementConsensus = hconsensus;
702   }
703
704   @Override
705   public Hashtable[] getSequenceConsensusHash()
706   {
707     return hconsensus;
708   }
709
710   @Override
711   public Hashtable[] getComplementConsensusHash()
712   {
713     return hcomplementConsensus;
714   }
715
716   @Override
717   public Hashtable[] getRnaStructureConsensusHash()
718   {
719     return hStrucConsensus;
720   }
721
722   @Override
723   public void setRnaStructureConsensusHash(Hashtable[] hStrucConsensus)
724   {
725     this.hStrucConsensus = hStrucConsensus;
726
727   }
728
729   @Override
730   public AlignmentAnnotation getAlignmentQualityAnnot()
731   {
732     return quality;
733   }
734
735   @Override
736   public AlignmentAnnotation getAlignmentConservationAnnotation()
737   {
738     return conservation;
739   }
740
741   @Override
742   public AlignmentAnnotation getAlignmentConsensusAnnotation()
743   {
744     return consensus;
745   }
746
747   @Override
748   public AlignmentAnnotation getComplementConsensusAnnotation()
749   {
750     return complementConsensus;
751   }
752
753   @Override
754   public AlignmentAnnotation getAlignmentStrucConsensusAnnotation()
755   {
756     return strucConsensus;
757   }
758
759   protected AlignCalcManagerI calculator = new AlignCalcManager();
760
761   /**
762    * trigger update of conservation annotation
763    */
764   public void updateConservation(final AlignmentViewPanel ap)
765   {
766     // see note in mantis : issue number 8585
767     if (alignment.isNucleotide() || conservation == null
768             || !autoCalculateConsensus)
769     {
770       return;
771     }
772     if (calculator
773             .getRegisteredWorkersOfClass(jalview.workers.ConservationThread.class) == null)
774     {
775       calculator.registerWorker(new jalview.workers.ConservationThread(
776               this, ap));
777     }
778   }
779
780   /**
781    * trigger update of consensus annotation
782    */
783   public void updateConsensus(final AlignmentViewPanel ap)
784   {
785     // see note in mantis : issue number 8585
786     if (consensus == null || !autoCalculateConsensus)
787     {
788       return;
789     }
790     if (calculator.getRegisteredWorkersOfClass(ConsensusThread.class) == null)
791     {
792       calculator.registerWorker(new ConsensusThread(this, ap));
793     }
794
795     /*
796      * A separate thread to compute cDNA consensus for a protein alignment
797      */
798     final AlignmentI al = this.getAlignment();
799     if (!al.isNucleotide() && al.getCodonFrames() != null
800             && !al.getCodonFrames().isEmpty())
801     {
802       if (calculator
803               .getRegisteredWorkersOfClass(ComplementConsensusThread.class) == null)
804       {
805         calculator.registerWorker(new ComplementConsensusThread(this, ap));
806       }
807     }
808   }
809
810   // --------START Structure Conservation
811   public void updateStrucConsensus(final AlignmentViewPanel ap)
812   {
813     if (autoCalculateStrucConsensus && strucConsensus == null
814             && alignment.isNucleotide() && alignment.hasRNAStructure())
815     {
816       // secondary structure has been added - so init the consensus line
817       initRNAStructure();
818     }
819
820     // see note in mantis : issue number 8585
821     if (strucConsensus == null || !autoCalculateStrucConsensus)
822     {
823       return;
824     }
825     if (calculator.getRegisteredWorkersOfClass(StrucConsensusThread.class) == null)
826     {
827       calculator.registerWorker(new StrucConsensusThread(this, ap));
828     }
829   }
830
831   public boolean isCalcInProgress()
832   {
833     return calculator.isWorking();
834   }
835
836   @Override
837   public boolean isCalculationInProgress(
838           AlignmentAnnotation alignmentAnnotation)
839   {
840     if (!alignmentAnnotation.autoCalculated)
841     {
842       return false;
843     }
844     if (calculator.workingInvolvedWith(alignmentAnnotation))
845     {
846       // System.err.println("grey out ("+alignmentAnnotation.label+")");
847       return true;
848     }
849     return false;
850   }
851
852   @Override
853   public boolean isClosed()
854   {
855     // TODO: check that this isClosed is only true after panel is closed, not
856     // before it is fully constructed.
857     return alignment == null;
858   }
859
860   @Override
861   public AlignCalcManagerI getCalcManager()
862   {
863     return calculator;
864   }
865
866   /**
867    * should conservation rows be shown for groups
868    */
869   protected boolean showGroupConservation = false;
870
871   /**
872    * should consensus rows be shown for groups
873    */
874   protected boolean showGroupConsensus = false;
875
876   /**
877    * should consensus profile be rendered by default
878    */
879   protected boolean showSequenceLogo = false;
880
881   /**
882    * should consensus profile be rendered normalised to row height
883    */
884   protected boolean normaliseSequenceLogo = false;
885
886   /**
887    * should consensus histograms be rendered by default
888    */
889   protected boolean showConsensusHistogram = true;
890
891   /**
892    * @return the showConsensusProfile
893    */
894   @Override
895   public boolean isShowSequenceLogo()
896   {
897     return showSequenceLogo;
898   }
899
900   /**
901    * @param showSequenceLogo
902    *          the new value
903    */
904   public void setShowSequenceLogo(boolean showSequenceLogo)
905   {
906     if (showSequenceLogo != this.showSequenceLogo)
907     {
908       // TODO: decouple settings setting from calculation when refactoring
909       // annotation update method from alignframe to viewport
910       this.showSequenceLogo = showSequenceLogo;
911       calculator.updateAnnotationFor(ConsensusThread.class);
912       calculator.updateAnnotationFor(ComplementConsensusThread.class);
913       calculator.updateAnnotationFor(StrucConsensusThread.class);
914     }
915     this.showSequenceLogo = showSequenceLogo;
916   }
917
918   /**
919    * @param showConsensusHistogram
920    *          the showConsensusHistogram to set
921    */
922   public void setShowConsensusHistogram(boolean showConsensusHistogram)
923   {
924     this.showConsensusHistogram = showConsensusHistogram;
925   }
926
927   /**
928    * @return the showGroupConservation
929    */
930   public boolean isShowGroupConservation()
931   {
932     return showGroupConservation;
933   }
934
935   /**
936    * @param showGroupConservation
937    *          the showGroupConservation to set
938    */
939   public void setShowGroupConservation(boolean showGroupConservation)
940   {
941     this.showGroupConservation = showGroupConservation;
942   }
943
944   /**
945    * @return the showGroupConsensus
946    */
947   public boolean isShowGroupConsensus()
948   {
949     return showGroupConsensus;
950   }
951
952   /**
953    * @param showGroupConsensus
954    *          the showGroupConsensus to set
955    */
956   public void setShowGroupConsensus(boolean showGroupConsensus)
957   {
958     this.showGroupConsensus = showGroupConsensus;
959   }
960
961   /**
962    * 
963    * @return flag to indicate if the consensus histogram should be rendered by
964    *         default
965    */
966   @Override
967   public boolean isShowConsensusHistogram()
968   {
969     return this.showConsensusHistogram;
970   }
971
972   /**
973    * when set, updateAlignment will always ensure sequences are of equal length
974    */
975   private boolean padGaps = false;
976
977   /**
978    * when set, alignment should be reordered according to a newly opened tree
979    */
980   public boolean sortByTree = false;
981
982
983   /**
984    * 
985    * 
986    * @return null or the currently selected sequence region
987    */
988   @Override
989   public SequenceGroup getSelectionGroup()
990   {
991     return selectionGroup;
992   }
993
994   /**
995    * Set the selection group for this window.
996    * 
997    * @param sg
998    *          - group holding references to sequences in this alignment view
999    * 
1000    */
1001   @Override
1002   public void setSelectionGroup(SequenceGroup sg)
1003   {
1004     selectionGroup = sg;
1005   }
1006
1007   public void setHiddenColumns(ColumnSelection colsel)
1008   {
1009     this.colSel = colsel;
1010   }
1011
1012   @Override
1013   public ColumnSelection getColumnSelection()
1014   {
1015     return colSel;
1016   }
1017
1018   @Override
1019   public void setColumnSelection(ColumnSelection colSel)
1020   {
1021     this.colSel = colSel;
1022     if (colSel != null)
1023     {
1024       updateHiddenColumns();
1025     }
1026   }
1027
1028   /**
1029    * 
1030    * @return
1031    */
1032   @Override
1033   public Map<SequenceI, SequenceCollectionI> getHiddenRepSequences()
1034   {
1035     return hiddenRepSequences;
1036   }
1037
1038   @Override
1039   public void setHiddenRepSequences(
1040           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
1041   {
1042     this.hiddenRepSequences = hiddenRepSequences;
1043   }
1044
1045   @Override
1046   public boolean hasHiddenColumns()
1047   {
1048     return colSel != null && colSel.hasHiddenColumns();
1049   }
1050
1051   public void updateHiddenColumns()
1052   {
1053     // this method doesn't really do anything now. But - it could, since a
1054     // column Selection could be in the process of modification
1055     // hasHiddenColumns = colSel.hasHiddenColumns();
1056   }
1057
1058   @Override
1059   public boolean hasHiddenRows()
1060   {
1061     return alignment.getHiddenSequences().getSize() > 0;
1062   }
1063
1064
1065   protected SequenceGroup selectionGroup;
1066
1067   public void setSequenceSetId(String newid)
1068   {
1069     if (sequenceSetID != null)
1070     {
1071       System.err
1072               .println("Warning - overwriting a sequenceSetId for a viewport!");
1073     }
1074     sequenceSetID = new String(newid);
1075   }
1076
1077   @Override
1078   public String getSequenceSetId()
1079   {
1080     if (sequenceSetID == null)
1081     {
1082       sequenceSetID = alignment.hashCode() + "";
1083     }
1084
1085     return sequenceSetID;
1086   }
1087
1088   /**
1089    * unique viewId for synchronizing state (e.g. with stored Jalview Project)
1090    * 
1091    */
1092   protected String viewId = null;
1093
1094   @Override
1095   public String getViewId()
1096   {
1097     if (viewId == null)
1098     {
1099       viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
1100     }
1101     return viewId;
1102   }
1103
1104   public void setIgnoreGapsConsensus(boolean b, AlignmentViewPanel ap)
1105   {
1106     ignoreGapsInConsensusCalculation = b;
1107     if (ap != null)
1108     {
1109       updateConsensus(ap);
1110       if (globalColourScheme != null)
1111       {
1112         globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
1113                 ignoreGapsInConsensusCalculation);
1114       }
1115     }
1116
1117   }
1118
1119   private long sgrouphash = -1, colselhash = -1;
1120
1121   /**
1122    * checks current SelectionGroup against record of last hash value, and
1123    * updates record.
1124    * 
1125    * @param b
1126    *          update the record of last hash value
1127    * 
1128    * @return true if SelectionGroup changed since last call (when b is true)
1129    */
1130   public boolean isSelectionGroupChanged(boolean b)
1131   {
1132     int hc = (selectionGroup == null || selectionGroup.getSize() == 0) ? -1
1133             : selectionGroup.hashCode();
1134     if (hc != -1 && hc != sgrouphash)
1135     {
1136       if (b)
1137       {
1138         sgrouphash = hc;
1139       }
1140       return true;
1141     }
1142     return false;
1143   }
1144
1145   /**
1146    * checks current colsel against record of last hash value, and optionally
1147    * updates record.
1148    * 
1149    * @param b
1150    *          update the record of last hash value
1151    * @return true if colsel changed since last call (when b is true)
1152    */
1153   public boolean isColSelChanged(boolean b)
1154   {
1155     int hc = (colSel == null || colSel.size() == 0) ? -1 : colSel
1156             .hashCode();
1157     if (hc != -1 && hc != colselhash)
1158     {
1159       if (b)
1160       {
1161         colselhash = hc;
1162       }
1163       return true;
1164     }
1165     return false;
1166   }
1167
1168   @Override
1169   public boolean isIgnoreGapsConsensus()
1170   {
1171     return ignoreGapsInConsensusCalculation;
1172   }
1173
1174   // / property change stuff
1175
1176   // JBPNote Prolly only need this in the applet version.
1177   private final java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
1178           this);
1179
1180   protected boolean showConservation = true;
1181
1182   protected boolean showQuality = true;
1183
1184   protected boolean showConsensus = true;
1185
1186   private Map<SequenceI, Color> sequenceColours = new HashMap<SequenceI, Color>();
1187
1188   protected SequenceAnnotationOrder sortAnnotationsBy = null;
1189
1190   protected boolean showAutocalculatedAbove;
1191
1192   /**
1193    * when set, view will scroll to show the highlighted position
1194    */
1195   private boolean followHighlight = true;
1196
1197   // TODO private with getters and setters?
1198   public int startRes;
1199
1200   public int endRes;
1201
1202   public int startSeq;
1203
1204   public int endSeq;
1205
1206   /**
1207    * Property change listener for changes in alignment
1208    * 
1209    * @param listener
1210    *          DOCUMENT ME!
1211    */
1212   public void addPropertyChangeListener(
1213           java.beans.PropertyChangeListener listener)
1214   {
1215     changeSupport.addPropertyChangeListener(listener);
1216   }
1217
1218   /**
1219    * DOCUMENT ME!
1220    * 
1221    * @param listener
1222    *          DOCUMENT ME!
1223    */
1224   public void removePropertyChangeListener(
1225           java.beans.PropertyChangeListener listener)
1226   {
1227     changeSupport.removePropertyChangeListener(listener);
1228   }
1229
1230   /**
1231    * Property change listener for changes in alignment
1232    * 
1233    * @param prop
1234    *          DOCUMENT ME!
1235    * @param oldvalue
1236    *          DOCUMENT ME!
1237    * @param newvalue
1238    *          DOCUMENT ME!
1239    */
1240   public void firePropertyChange(String prop, Object oldvalue,
1241           Object newvalue)
1242   {
1243     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
1244   }
1245
1246   // common hide/show column stuff
1247
1248   public void hideSelectedColumns()
1249   {
1250     if (colSel.size() < 1)
1251     {
1252       return;
1253     }
1254
1255     colSel.hideSelectedColumns();
1256     setSelectionGroup(null);
1257
1258   }
1259
1260   public void hideColumns(int start, int end)
1261   {
1262     if (start == end)
1263     {
1264       colSel.hideColumns(start);
1265     }
1266     else
1267     {
1268       colSel.hideColumns(start, end);
1269     }
1270   }
1271
1272   public void showColumn(int col)
1273   {
1274     colSel.revealHiddenColumns(col);
1275
1276   }
1277
1278   public void showAllHiddenColumns()
1279   {
1280     colSel.revealAllHiddenColumns();
1281   }
1282
1283   // common hide/show seq stuff
1284   public void showAllHiddenSeqs()
1285   {
1286     if (alignment.getHiddenSequences().getSize() > 0)
1287     {
1288       if (selectionGroup == null)
1289       {
1290         selectionGroup = new SequenceGroup();
1291         selectionGroup.setEndRes(alignment.getWidth() - 1);
1292       }
1293       List<SequenceI> tmp = alignment.getHiddenSequences().showAll(
1294               hiddenRepSequences);
1295       for (SequenceI seq : tmp)
1296       {
1297         selectionGroup.addSequence(seq, false);
1298         setSequenceAnnotationsVisible(seq, true);
1299       }
1300
1301       hiddenRepSequences = null;
1302
1303       firePropertyChange("alignment", null, alignment.getSequences());
1304       // used to set hasHiddenRows/hiddenRepSequences here, after the property
1305       // changed event
1306       sendSelection();
1307     }
1308   }
1309
1310   public void showSequence(int index)
1311   {
1312     List<SequenceI> tmp = alignment.getHiddenSequences().showSequence(
1313             index,
1314             hiddenRepSequences);
1315     if (tmp.size() > 0)
1316     {
1317       if (selectionGroup == null)
1318       {
1319         selectionGroup = new SequenceGroup();
1320         selectionGroup.setEndRes(alignment.getWidth() - 1);
1321       }
1322
1323       for (SequenceI seq : tmp)
1324       {
1325         selectionGroup.addSequence(seq, false);
1326         setSequenceAnnotationsVisible(seq, true);
1327       }
1328       firePropertyChange("alignment", null, alignment.getSequences());
1329       sendSelection();
1330     }
1331   }
1332
1333   public void hideAllSelectedSeqs()
1334   {
1335     if (selectionGroup == null || selectionGroup.getSize() < 1)
1336     {
1337       return;
1338     }
1339
1340     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
1341
1342     hideSequence(seqs);
1343
1344     setSelectionGroup(null);
1345   }
1346
1347   public void hideSequence(SequenceI[] seq)
1348   {
1349     if (seq != null)
1350     {
1351       for (int i = 0; i < seq.length; i++)
1352       {
1353         alignment.getHiddenSequences().hideSequence(seq[i]);
1354         setSequenceAnnotationsVisible(seq[i], false);
1355       }
1356       firePropertyChange("alignment", null, alignment.getSequences());
1357     }
1358   }
1359
1360   /**
1361    * Set visibility for any annotations for the given sequence.
1362    * 
1363    * @param sequenceI
1364    */
1365   protected void setSequenceAnnotationsVisible(SequenceI sequenceI,
1366           boolean visible)
1367   {
1368     for (AlignmentAnnotation ann : alignment.getAlignmentAnnotation())
1369     {
1370       if (ann.sequenceRef == sequenceI)
1371       {
1372         ann.visible = visible;
1373       }
1374     }
1375   }
1376
1377   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
1378   {
1379     int sSize = sg.getSize();
1380     if (sSize < 2)
1381     {
1382       return;
1383     }
1384
1385     if (hiddenRepSequences == null)
1386     {
1387       hiddenRepSequences = new Hashtable();
1388     }
1389
1390     hiddenRepSequences.put(repSequence, sg);
1391
1392     // Hide all sequences except the repSequence
1393     SequenceI[] seqs = new SequenceI[sSize - 1];
1394     int index = 0;
1395     for (int i = 0; i < sSize; i++)
1396     {
1397       if (sg.getSequenceAt(i) != repSequence)
1398       {
1399         if (index == sSize - 1)
1400         {
1401           return;
1402         }
1403
1404         seqs[index++] = sg.getSequenceAt(i);
1405       }
1406     }
1407     sg.setSeqrep(repSequence); // note: not done in 2.7applet
1408     sg.setHidereps(true); // note: not done in 2.7applet
1409     hideSequence(seqs);
1410
1411   }
1412
1413   public boolean isHiddenRepSequence(SequenceI seq)
1414   {
1415     return alignment.getSeqrep()==seq || (hiddenRepSequences != null
1416             && hiddenRepSequences.containsKey(seq));
1417   }
1418
1419   public SequenceGroup getRepresentedSequences(SequenceI seq)
1420   {
1421     return (SequenceGroup) (hiddenRepSequences == null ? null
1422             : hiddenRepSequences.get(seq));
1423   }
1424
1425   @Override
1426   public int adjustForHiddenSeqs(int alignmentIndex)
1427   {
1428     return alignment.getHiddenSequences().adjustForHiddenSeqs(
1429             alignmentIndex);
1430   }
1431
1432   @Override
1433   public void invertColumnSelection()
1434   {
1435     colSel.invertColumnSelection(0, alignment.getWidth());
1436   }
1437
1438
1439   @Override
1440   public SequenceI[] getSelectionAsNewSequence()
1441   {
1442     SequenceI[] sequences;
1443     // JBPNote: Need to test jalviewLite.getSelectedSequencesAsAlignmentFrom -
1444     // this was the only caller in the applet for this method
1445     // JBPNote: in applet, this method returned references to the alignment
1446     // sequences, and it did not honour the presence/absence of annotation
1447     // attached to the alignment (probably!)
1448     if (selectionGroup == null || selectionGroup.getSize() == 0)
1449     {
1450       sequences = alignment.getSequencesArray();
1451       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
1452       for (int i = 0; i < sequences.length; i++)
1453       {
1454         // construct new sequence with subset of visible annotation
1455         sequences[i] = new Sequence(sequences[i], annots);
1456       }
1457     }
1458     else
1459     {
1460       sequences = selectionGroup.getSelectionAsNewSequences(alignment);
1461     }
1462
1463     return sequences;
1464   }
1465
1466
1467   @Override
1468   public SequenceI[] getSequenceSelection()
1469   {
1470     SequenceI[] sequences = null;
1471     if (selectionGroup != null)
1472     {
1473       sequences = selectionGroup.getSequencesInOrder(alignment);
1474     }
1475     if (sequences == null)
1476     {
1477       sequences = alignment.getSequencesArray();
1478     }
1479     return sequences;
1480   }
1481
1482
1483   @Override
1484   public CigarArray getViewAsCigars(
1485           boolean selectedRegionOnly)
1486   {
1487     return new CigarArray(alignment, colSel,
1488             (selectedRegionOnly ? selectionGroup : null));
1489   }
1490
1491
1492   @Override
1493   public jalview.datamodel.AlignmentView getAlignmentView(
1494           boolean selectedOnly)
1495   {
1496     return getAlignmentView(selectedOnly, false);
1497   }
1498
1499
1500   @Override
1501   public jalview.datamodel.AlignmentView getAlignmentView(
1502           boolean selectedOnly, boolean markGroups)
1503   {
1504     return new AlignmentView(alignment, colSel, selectionGroup,
1505             colSel != null && colSel.hasHiddenColumns(), selectedOnly,
1506             markGroups);
1507   }
1508
1509
1510   @Override
1511   public String[] getViewAsString(boolean selectedRegionOnly)
1512   {
1513     String[] selection = null;
1514     SequenceI[] seqs = null;
1515     int i, iSize;
1516     int start = 0, end = 0;
1517     if (selectedRegionOnly && selectionGroup != null)
1518     {
1519       iSize = selectionGroup.getSize();
1520       seqs = selectionGroup.getSequencesInOrder(alignment);
1521       start = selectionGroup.getStartRes();
1522       end = selectionGroup.getEndRes() + 1;
1523     }
1524     else
1525     {
1526       iSize = alignment.getHeight();
1527       seqs = alignment.getSequencesArray();
1528       end = alignment.getWidth();
1529     }
1530
1531     selection = new String[iSize];
1532     if (colSel != null && colSel.hasHiddenColumns())
1533     {
1534       selection = colSel.getVisibleSequenceStrings(start, end, seqs);
1535     }
1536     else
1537     {
1538       for (i = 0; i < iSize; i++)
1539       {
1540         selection[i] = seqs[i].getSequenceAsString(start, end);
1541       }
1542
1543     }
1544     return selection;
1545   }
1546
1547
1548   @Override
1549   public List<int[]> getVisibleRegionBoundaries(int min, int max)
1550   {
1551     ArrayList<int[]> regions = new ArrayList<int[]>();
1552     int start = min;
1553     int end = max;
1554
1555     do
1556     {
1557       if (colSel != null && colSel.hasHiddenColumns())
1558       {
1559         if (start == 0)
1560         {
1561           start = colSel.adjustForHiddenColumns(start);
1562         }
1563
1564         end = colSel.getHiddenBoundaryRight(start);
1565         if (start == end)
1566         {
1567           end = max;
1568         }
1569         if (end > max)
1570         {
1571           end = max;
1572         }
1573       }
1574
1575       regions.add(new int[]
1576       { start, end });
1577
1578       if (colSel != null && colSel.hasHiddenColumns())
1579       {
1580         start = colSel.adjustForHiddenColumns(end);
1581         start = colSel.getHiddenBoundaryLeft(start) + 1;
1582       }
1583     } while (end < max);
1584
1585     int[][] startEnd = new int[regions.size()][2];
1586
1587     return regions;
1588   }
1589
1590   @Override
1591   public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(boolean selectedOnly)
1592   {
1593     ArrayList<AlignmentAnnotation> ala = new ArrayList<AlignmentAnnotation>();
1594     AlignmentAnnotation[] aa;
1595     if ((aa=alignment.getAlignmentAnnotation())!=null)
1596     {
1597       for (AlignmentAnnotation annot:aa)
1598       {
1599         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
1600         if (selectedOnly && selectionGroup!=null)
1601         {
1602           colSel.makeVisibleAnnotation(selectionGroup.getStartRes(), selectionGroup.getEndRes(),clone);
1603         } else {
1604           colSel.makeVisibleAnnotation(clone);
1605         }
1606         ala.add(clone);
1607       }
1608     }
1609     return ala;
1610   }
1611
1612
1613   @Override
1614   public boolean isPadGaps()
1615   {
1616     return padGaps;
1617   }
1618
1619
1620   @Override
1621   public void setPadGaps(boolean padGaps)
1622   {
1623     this.padGaps = padGaps;
1624   }
1625
1626   /**
1627    * apply any post-edit constraints and trigger any calculations needed after
1628    * an edit has been performed on the alignment
1629    * 
1630    * @param ap
1631    */
1632   @Override
1633   public void alignmentChanged(AlignmentViewPanel ap)
1634   {
1635     if (isPadGaps())
1636     {
1637       alignment.padGaps();
1638     }
1639     if (autoCalculateConsensus)
1640     {
1641       updateConsensus(ap);
1642     }
1643     if (hconsensus != null && autoCalculateConsensus)
1644     {
1645       updateConservation(ap);
1646     }
1647     if (autoCalculateStrucConsensus)
1648     {
1649       updateStrucConsensus(ap);
1650     }
1651
1652     // Reset endRes of groups if beyond alignment width
1653     int alWidth = alignment.getWidth();
1654     List<SequenceGroup> groups = alignment.getGroups();
1655     if (groups != null)
1656     {
1657       for (SequenceGroup sg : groups)
1658       {
1659         if (sg.getEndRes() > alWidth)
1660         {
1661           sg.setEndRes(alWidth - 1);
1662         }
1663       }
1664     }
1665
1666     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1667     {
1668       selectionGroup.setEndRes(alWidth - 1);
1669     }
1670
1671     resetAllColourSchemes();
1672     calculator.restartWorkers();
1673     // alignment.adjustSequenceAnnotations();
1674   }
1675
1676   /**
1677    * reset scope and do calculations for all applied colourschemes on alignment
1678    */
1679   void resetAllColourSchemes()
1680   {
1681     ColourSchemeI cs = globalColourScheme;
1682     if (cs != null)
1683     {
1684       cs.alignmentChanged(alignment, hiddenRepSequences);
1685
1686       cs.setConsensus(hconsensus);
1687       if (cs.conservationApplied())
1688       {
1689         cs.setConservation(Conservation.calculateConservation("All",
1690                 ResidueProperties.propHash, 3, alignment.getSequences(), 0,
1691                 alignment.getWidth(), false, getConsPercGaps(), false));
1692       }
1693     }
1694
1695     for (SequenceGroup sg : alignment.getGroups())
1696     {
1697       if (sg.cs != null)
1698       {
1699         sg.cs.alignmentChanged(sg, hiddenRepSequences);
1700       }
1701       sg.recalcConservation();
1702     }
1703   }
1704
1705   protected void initAutoAnnotation()
1706   {
1707     // TODO: add menu option action that nulls or creates consensus object
1708     // depending on if the user wants to see the annotation or not in a
1709     // specific alignment
1710
1711     if (hconsensus == null && !isDataset)
1712     {
1713       if (!alignment.isNucleotide())
1714       {
1715         initConservation();
1716         initQuality();
1717       }
1718       else
1719       {
1720         initRNAStructure();
1721       }
1722       consensus = new AlignmentAnnotation("Consensus", "PID",
1723               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
1724       initConsensus(consensus);
1725
1726       initComplementConsensus();
1727     }
1728   }
1729
1730   /**
1731    * If this is a protein alignment and there are mappings to cDNA, add the cDNA
1732    * consensus annotation.
1733    */
1734   public void initComplementConsensus()
1735   {
1736     if (!alignment.isNucleotide())
1737     {
1738       final Set<AlignedCodonFrame> codonMappings = alignment
1739               .getCodonFrames();
1740       if (codonMappings != null && !codonMappings.isEmpty())
1741       {
1742         complementConsensus = new AlignmentAnnotation("cDNA Consensus",
1743                 "PID for cDNA", new Annotation[1], 0f, 100f,
1744                 AlignmentAnnotation.BAR_GRAPH);
1745         initConsensus(complementConsensus);
1746       }
1747     }
1748   }
1749
1750   private void initConsensus(AlignmentAnnotation aa)
1751   {
1752     aa.hasText = true;
1753     aa.autoCalculated = true;
1754
1755     if (showConsensus)
1756     {
1757       alignment.addAnnotation(aa);
1758     }
1759   }
1760
1761   private void initConservation()
1762   {
1763     if (showConservation)
1764     {
1765       if (conservation == null)
1766       {
1767         conservation = new AlignmentAnnotation("Conservation",
1768                 "Conservation of total alignment less than "
1769                         + getConsPercGaps() + "% gaps", new Annotation[1],
1770                 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
1771         conservation.hasText = true;
1772         conservation.autoCalculated = true;
1773         alignment.addAnnotation(conservation);
1774       }
1775     }
1776   }
1777
1778   private void initQuality()
1779   {
1780     if (showQuality)
1781     {
1782       if (quality == null)
1783       {
1784         quality = new AlignmentAnnotation("Quality",
1785                 "Alignment Quality based on Blosum62 scores",
1786                 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
1787         quality.hasText = true;
1788         quality.autoCalculated = true;
1789         alignment.addAnnotation(quality);
1790       }
1791     }
1792   }
1793
1794   private void initRNAStructure()
1795   {
1796     if (alignment.hasRNAStructure() && strucConsensus == null)
1797     {
1798       strucConsensus = new AlignmentAnnotation("StrucConsensus", "PID",
1799               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
1800       strucConsensus.hasText = true;
1801       strucConsensus.autoCalculated = true;
1802
1803       if (showConsensus)
1804       {
1805         alignment.addAnnotation(strucConsensus);
1806       }
1807     }
1808   }
1809
1810   /*
1811    * (non-Javadoc)
1812    * 
1813    * @see jalview.api.AlignViewportI#calcPanelHeight()
1814    */
1815   @Override
1816   public int calcPanelHeight()
1817   {
1818     // setHeight of panels
1819     AlignmentAnnotation[] anns = getAlignment().getAlignmentAnnotation();
1820     int height = 0;
1821     int charHeight = getCharHeight();
1822     if (anns != null)
1823     {
1824       BitSet graphgrp = new BitSet();
1825       for (AlignmentAnnotation aa : anns)
1826       {
1827         if (aa == null)
1828         {
1829           System.err.println("Null annotation row: ignoring.");
1830           continue;
1831         }
1832         if (!aa.visible)
1833         {
1834           continue;
1835         }
1836         if (aa.graphGroup > -1)
1837         {
1838           if (graphgrp.get(aa.graphGroup))
1839           {
1840             continue;
1841           }
1842           else
1843           {
1844             graphgrp.set(aa.graphGroup);
1845           }
1846         }
1847         aa.height = 0;
1848
1849         if (aa.hasText)
1850         {
1851           aa.height += charHeight;
1852         }
1853
1854         if (aa.hasIcons)
1855         {
1856           aa.height += 16;
1857         }
1858
1859         if (aa.graph > 0)
1860         {
1861           aa.height += aa.graphHeight;
1862         }
1863
1864         if (aa.height == 0)
1865         {
1866           aa.height = 20;
1867         }
1868
1869         height += aa.height;
1870       }
1871     }
1872     if (height == 0)
1873     {
1874       // set minimum
1875       height = 20;
1876     }
1877     return height;
1878   }
1879
1880   @Override
1881   public void updateGroupAnnotationSettings(boolean applyGlobalSettings,
1882           boolean preserveNewGroupSettings)
1883   {
1884     boolean updateCalcs = false;
1885     boolean conv = isShowGroupConservation();
1886     boolean cons = isShowGroupConsensus();
1887     boolean showprf = isShowSequenceLogo();
1888     boolean showConsHist = isShowConsensusHistogram();
1889     boolean normLogo = isNormaliseSequenceLogo();
1890
1891     /**
1892      * TODO reorder the annotation rows according to group/sequence ordering on
1893      * alignment
1894      */
1895     boolean sortg = true;
1896
1897     // remove old automatic annotation
1898     // add any new annotation
1899
1900     // intersect alignment annotation with alignment groups
1901
1902     AlignmentAnnotation[] aan = alignment.getAlignmentAnnotation();
1903     List<SequenceGroup> oldrfs = new ArrayList<SequenceGroup>();
1904     if (aan != null)
1905     {
1906       for (int an = 0; an < aan.length; an++)
1907       {
1908         if (aan[an].autoCalculated && aan[an].groupRef != null)
1909         {
1910           oldrfs.add(aan[an].groupRef);
1911           alignment.deleteAnnotation(aan[an], false);
1912         }
1913       }
1914     }
1915     if (alignment.getGroups() != null)
1916     {
1917       for (SequenceGroup sg : alignment.getGroups())
1918       {
1919         updateCalcs = false;
1920         if (applyGlobalSettings
1921                 || (!preserveNewGroupSettings && !oldrfs.contains(sg)))
1922         {
1923           // set defaults for this group's conservation/consensus
1924           sg.setshowSequenceLogo(showprf);
1925           sg.setShowConsensusHistogram(showConsHist);
1926           sg.setNormaliseSequenceLogo(normLogo);
1927         }
1928         if (conv)
1929         {
1930           updateCalcs = true;
1931           alignment.addAnnotation(sg.getConservationRow(), 0);
1932         }
1933         if (cons)
1934         {
1935           updateCalcs = true;
1936           alignment.addAnnotation(sg.getConsensus(), 0);
1937         }
1938         // refresh the annotation rows
1939         if (updateCalcs)
1940         {
1941           sg.recalcConservation();
1942         }
1943       }
1944     }
1945     oldrfs.clear();
1946   }
1947   @Override
1948   public boolean isDisplayReferenceSeq()
1949   {
1950     return alignment.hasSeqrep() && viewStyle.isDisplayReferenceSeq();
1951   }
1952
1953   @Override
1954   public void setDisplayReferenceSeq(boolean displayReferenceSeq)
1955   {
1956     viewStyle.setDisplayReferenceSeq(displayReferenceSeq);
1957   }
1958
1959   @Override
1960   public boolean isColourByReferenceSeq()
1961   {
1962     return alignment.hasSeqrep() && viewStyle.isColourByReferenceSeq();
1963   }
1964
1965   @Override
1966   public Color getSequenceColour(SequenceI seq)
1967   {
1968     Color sqc = sequenceColours.get(seq);
1969     return (sqc == null ? Color.white : sqc);
1970   }
1971
1972   @Override
1973   public void setSequenceColour(SequenceI seq, Color col)
1974   {
1975     if (col == null)
1976     {
1977       sequenceColours.remove(seq);
1978     }
1979     else
1980     {
1981       sequenceColours.put(seq, col);
1982     }
1983   }
1984
1985   @Override
1986   public void updateSequenceIdColours()
1987   {
1988     for (SequenceGroup sg : alignment.getGroups())
1989     {
1990       if (sg.idColour != null)
1991       {
1992         for (SequenceI s : sg.getSequences(getHiddenRepSequences()))
1993         {
1994           sequenceColours.put(s, sg.idColour);
1995         }
1996       }
1997     }
1998   }
1999
2000   @Override
2001   public void clearSequenceColours()
2002   {
2003     sequenceColours.clear();
2004   };
2005
2006   @Override
2007   public AlignViewportI getCodingComplement()
2008   {
2009     return this.codingComplement;
2010   }
2011
2012   /**
2013    * Set this as the (cDna/protein) complement of the given viewport. Also
2014    * ensures the reverse relationship is set on the given viewport.
2015    */
2016   @Override
2017   public void setCodingComplement(AlignViewportI av)
2018   {
2019     if (this == av)
2020     {
2021       System.err.println("Ignoring recursive setCodingComplement request");
2022     }
2023     else
2024     {
2025       this.codingComplement = av;
2026       // avoid infinite recursion!
2027       if (av.getCodingComplement() != this)
2028       {
2029         av.setCodingComplement(this);
2030       }
2031     }
2032   }
2033
2034   @Override
2035   public boolean isNucleotide()
2036   {
2037     return getAlignment() == null ? false : getAlignment().isNucleotide();
2038   }
2039
2040   @Override
2041   public FeaturesDisplayedI getFeaturesDisplayed()
2042   {
2043     return featuresDisplayed;
2044   }
2045
2046   @Override
2047   public void setFeaturesDisplayed(FeaturesDisplayedI featuresDisplayedI)
2048   {
2049     featuresDisplayed = featuresDisplayedI;
2050   }
2051
2052   @Override
2053   public boolean areFeaturesDisplayed()
2054   {
2055     return featuresDisplayed != null && featuresDisplayed.getRegisterdFeaturesCount()>0;
2056   }
2057
2058   /**
2059    * set the flag
2060    * 
2061    * @param b
2062    *          features are displayed if true
2063    */
2064   @Override
2065   public void setShowSequenceFeatures(boolean b)
2066   {
2067     viewStyle.setShowSequenceFeatures(b);
2068   }
2069   @Override
2070   public boolean isShowSequenceFeatures()
2071   {
2072     return viewStyle.isShowSequenceFeatures();
2073   }
2074
2075   @Override
2076   public void setShowSequenceFeaturesHeight(boolean selected)
2077   {
2078     viewStyle.setShowSequenceFeaturesHeight(selected);
2079   }
2080
2081   @Override
2082   public boolean isShowSequenceFeaturesHeight()
2083   {
2084     return viewStyle.isShowSequenceFeaturesHeight();
2085   }
2086
2087
2088
2089   @Override
2090   public void setShowAnnotation(boolean b)
2091   {
2092     viewStyle.setShowAnnotation(b);
2093   }
2094
2095   @Override
2096   public boolean isShowAnnotation()
2097   {
2098     return viewStyle.isShowAnnotation();
2099   }
2100
2101   @Override
2102   public boolean isRightAlignIds()
2103   {
2104     return viewStyle.isRightAlignIds();
2105   }
2106
2107   @Override
2108   public void setRightAlignIds(boolean rightAlignIds)
2109   {
2110     viewStyle.setRightAlignIds(rightAlignIds);
2111   }
2112
2113   @Override
2114   public boolean getConservationSelected()
2115   {
2116     return viewStyle.getConservationSelected();
2117   }
2118
2119   @Override
2120   public void setShowBoxes(boolean state)
2121   {
2122     viewStyle.setShowBoxes(state);
2123   }
2124
2125   /**
2126    * @return
2127    * @see jalview.api.ViewStyleI#getTextColour()
2128    */
2129   public Color getTextColour()
2130   {
2131     return viewStyle.getTextColour();
2132   }
2133
2134   /**
2135    * @return
2136    * @see jalview.api.ViewStyleI#getTextColour2()
2137    */
2138   public Color getTextColour2()
2139   {
2140     return viewStyle.getTextColour2();
2141   }
2142
2143   /**
2144    * @return
2145    * @see jalview.api.ViewStyleI#getThresholdTextColour()
2146    */
2147   public int getThresholdTextColour()
2148   {
2149     return viewStyle.getThresholdTextColour();
2150   }
2151
2152   /**
2153    * @return
2154    * @see jalview.api.ViewStyleI#isConservationColourSelected()
2155    */
2156   public boolean isConservationColourSelected()
2157   {
2158     return viewStyle.isConservationColourSelected();
2159   }
2160
2161   /**
2162    * @return
2163    * @see jalview.api.ViewStyleI#isRenderGaps()
2164    */
2165   public boolean isRenderGaps()
2166   {
2167     return viewStyle.isRenderGaps();
2168   }
2169
2170   /**
2171    * @return
2172    * @see jalview.api.ViewStyleI#isShowColourText()
2173    */
2174   public boolean isShowColourText()
2175   {
2176     return viewStyle.isShowColourText();
2177   }
2178
2179   /**
2180    * @param conservationColourSelected
2181    * @see jalview.api.ViewStyleI#setConservationColourSelected(boolean)
2182    */
2183   public void setConservationColourSelected(
2184           boolean conservationColourSelected)
2185   {
2186     viewStyle.setConservationColourSelected(conservationColourSelected);
2187   }
2188
2189   /**
2190    * @param showColourText
2191    * @see jalview.api.ViewStyleI#setShowColourText(boolean)
2192    */
2193   public void setShowColourText(boolean showColourText)
2194   {
2195     viewStyle.setShowColourText(showColourText);
2196   }
2197
2198   /**
2199    * @param textColour
2200    * @see jalview.api.ViewStyleI#setTextColour(java.awt.Color)
2201    */
2202   public void setTextColour(Color textColour)
2203   {
2204     viewStyle.setTextColour(textColour);
2205   }
2206
2207   /**
2208    * @param thresholdTextColour
2209    * @see jalview.api.ViewStyleI#setThresholdTextColour(int)
2210    */
2211   public void setThresholdTextColour(int thresholdTextColour)
2212   {
2213     viewStyle.setThresholdTextColour(thresholdTextColour);
2214   }
2215
2216   /**
2217    * @param textColour2
2218    * @see jalview.api.ViewStyleI#setTextColour2(java.awt.Color)
2219    */
2220   public void setTextColour2(Color textColour2)
2221   {
2222     viewStyle.setTextColour2(textColour2);
2223   }
2224
2225   @Override
2226   public ViewStyleI getViewStyle()
2227   {
2228     return new ViewStyle(viewStyle);
2229   }
2230
2231   @Override
2232   public void setViewStyle(ViewStyleI settingsForView)
2233   {
2234     viewStyle = new ViewStyle(settingsForView);
2235   }
2236
2237   @Override
2238   public boolean sameStyle(ViewStyleI them)
2239   {
2240     return viewStyle.sameStyle(them);
2241   }
2242
2243   /**
2244    * @return
2245    * @see jalview.api.ViewStyleI#getIdWidth()
2246    */
2247   public int getIdWidth()
2248   {
2249     return viewStyle.getIdWidth();
2250   }
2251
2252   /**
2253    * @param i
2254    * @see jalview.api.ViewStyleI#setIdWidth(int)
2255    */
2256   public void setIdWidth(int i)
2257   {
2258     viewStyle.setIdWidth(i);
2259   }
2260
2261   /**
2262    * @return
2263    * @see jalview.api.ViewStyleI#isCentreColumnLabels()
2264    */
2265   public boolean isCentreColumnLabels()
2266   {
2267     return viewStyle.isCentreColumnLabels();
2268   }
2269
2270   /**
2271    * @param centreColumnLabels
2272    * @see jalview.api.ViewStyleI#setCentreColumnLabels(boolean)
2273    */
2274   public void setCentreColumnLabels(boolean centreColumnLabels)
2275   {
2276     viewStyle.setCentreColumnLabels(centreColumnLabels);
2277   }
2278
2279   /**
2280    * @param showdbrefs
2281    * @see jalview.api.ViewStyleI#setShowDBRefs(boolean)
2282    */
2283   public void setShowDBRefs(boolean showdbrefs)
2284   {
2285     viewStyle.setShowDBRefs(showdbrefs);
2286   }
2287
2288   /**
2289    * @return
2290    * @see jalview.api.ViewStyleI#isShowDBRefs()
2291    */
2292   public boolean isShowDBRefs()
2293   {
2294     return viewStyle.isShowDBRefs();
2295   }
2296
2297   /**
2298    * @return
2299    * @see jalview.api.ViewStyleI#isShowNPFeats()
2300    */
2301   public boolean isShowNPFeats()
2302   {
2303     return viewStyle.isShowNPFeats();
2304   }
2305
2306   /**
2307    * @param shownpfeats
2308    * @see jalview.api.ViewStyleI#setShowNPFeats(boolean)
2309    */
2310   public void setShowNPFeats(boolean shownpfeats)
2311   {
2312     viewStyle.setShowNPFeats(shownpfeats);
2313   }
2314
2315   public abstract StructureSelectionManager getStructureSelectionManager();
2316
2317   /**
2318    * Add one command to the command history list.
2319    * 
2320    * @param command
2321    */
2322   public void addToHistoryList(CommandI command)
2323   {
2324     if (this.historyList != null)
2325     {
2326       this.historyList.push(command);
2327       broadcastCommand(command, false);
2328     }
2329   }
2330
2331   protected void broadcastCommand(CommandI command, boolean undo)
2332   {
2333     getStructureSelectionManager().commandPerformed(command, undo, getVamsasSource());
2334   }
2335
2336   /**
2337    * Add one command to the command redo list.
2338    * 
2339    * @param command
2340    */
2341   public void addToRedoList(CommandI command)
2342   {
2343     if (this.redoList != null)
2344     {
2345       this.redoList.push(command);
2346     }
2347     broadcastCommand(command, true);
2348   }
2349
2350   /**
2351    * Clear the command redo list.
2352    */
2353   public void clearRedoList()
2354   {
2355     if (this.redoList != null)
2356     {
2357       this.redoList.clear();
2358     }
2359   }
2360
2361   public void setHistoryList(Deque<CommandI> list)
2362   {
2363     this.historyList = list;
2364   }
2365
2366   public Deque<CommandI> getHistoryList()
2367   {
2368     return this.historyList;
2369   }
2370
2371   public void setRedoList(Deque<CommandI> list)
2372   {
2373     this.redoList = list;
2374   }
2375
2376   public Deque<CommandI> getRedoList()
2377   {
2378     return this.redoList;
2379   }
2380
2381   @Override
2382   public VamsasSource getVamsasSource()
2383   {
2384     return this;
2385   }
2386
2387   public SequenceAnnotationOrder getSortAnnotationsBy()
2388   {
2389     return sortAnnotationsBy;
2390   }
2391
2392   public void setSortAnnotationsBy(SequenceAnnotationOrder sortAnnotationsBy)
2393   {
2394     this.sortAnnotationsBy = sortAnnotationsBy;
2395   }
2396
2397   public boolean isShowAutocalculatedAbove()
2398   {
2399     return showAutocalculatedAbove;
2400   }
2401
2402   public void setShowAutocalculatedAbove(boolean showAutocalculatedAbove)
2403   {
2404     this.showAutocalculatedAbove = showAutocalculatedAbove;
2405   }
2406
2407   @Override
2408   public boolean isScaleProteinAsCdna()
2409   {
2410     return viewStyle.isScaleProteinAsCdna();
2411   }
2412
2413   @Override
2414   public void setScaleProteinAsCdna(boolean b)
2415   {
2416     viewStyle.setScaleProteinAsCdna(b);
2417   }
2418
2419   /**
2420    * @return true if view should scroll to show the highlighted region of a
2421    *         sequence
2422    * @return
2423    */
2424   @Override
2425   public final boolean isFollowHighlight()
2426   {
2427     return followHighlight;
2428   }
2429
2430   @Override
2431   public final void setFollowHighlight(boolean b)
2432   {
2433     this.followHighlight = b;
2434   }
2435
2436   public int getStartRes()
2437   {
2438     return startRes;
2439   }
2440
2441   public int getEndRes()
2442   {
2443     return endRes;
2444   }
2445
2446   public int getStartSeq()
2447   {
2448     return startSeq;
2449   }
2450
2451   public void setStartRes(int res)
2452   {
2453     this.startRes = res;
2454   }
2455
2456   public void setStartSeq(int seq)
2457   {
2458     this.startSeq = seq;
2459   }
2460
2461   public void setEndRes(int res)
2462   {
2463     if (res > alignment.getWidth() - 1)
2464     {
2465       // log.System.out.println(" Corrected res from " + res + " to maximum " +
2466       // (alignment.getWidth()-1));
2467       res = alignment.getWidth() - 1;
2468     }
2469     if (res < 0)
2470     {
2471       res = 0;
2472     }
2473     this.endRes = res;
2474   }
2475
2476   public void setEndSeq(int seq)
2477   {
2478     if (seq > alignment.getHeight())
2479     {
2480       seq = alignment.getHeight();
2481     }
2482     if (seq < 0)
2483     {
2484       seq = 0;
2485     }
2486     this.endSeq = seq;
2487   }
2488
2489   public int getEndSeq()
2490   {
2491     return endSeq;
2492   }
2493
2494   /**
2495    * Helper method to populate the SearchResults with the location in the
2496    * complementary alignment to scroll to, in order to match this one.
2497    * 
2498    * @param sr
2499    *          the SearchResults to add to
2500    * @return the offset (below top of visible region) of the matched sequence
2501    */
2502   protected int findComplementScrollTarget(SearchResults sr)
2503   {
2504     final AlignViewportI codingComplement = getCodingComplement();
2505     if (codingComplement == null || !codingComplement.isFollowHighlight())
2506     {
2507       return 0;
2508     }
2509     boolean iAmProtein = !getAlignment().isNucleotide();
2510     AlignmentI proteinAlignment = iAmProtein ? getAlignment()
2511             : codingComplement.getAlignment();
2512     if (proteinAlignment == null)
2513     {
2514       return 0;
2515     }
2516     final Set<AlignedCodonFrame> mappings = proteinAlignment
2517             .getCodonFrames();
2518   
2519     /*
2520      * Heuristic: find the first mapped sequence (if any) with a non-gapped
2521      * residue in the middle column of the visible region. Scroll the
2522      * complementary alignment to line up the corresponding residue.
2523      */
2524     int seqOffset = 0;
2525     SequenceI sequence = null;
2526
2527     /*
2528      * locate 'middle' column (true middle if an odd number visible, left of
2529      * middle if an even number visible)
2530      */
2531     int middleColumn = getStartRes() + (getEndRes() - getStartRes()) / 2;
2532     final HiddenSequences hiddenSequences = getAlignment()
2533             .getHiddenSequences();
2534     for (int seqNo = getStartSeq(); seqNo < getEndSeq(); seqNo++, seqOffset++)
2535     {
2536       sequence = getAlignment().getSequenceAt(seqNo);
2537       if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
2538       {
2539         continue;
2540       }
2541       if (Comparison.isGap(sequence.getCharAt(middleColumn)))
2542       {
2543         continue;
2544       }
2545       List<AlignedCodonFrame> seqMappings = MappingUtils
2546               .findMappingsForSequence(sequence, mappings);
2547       if (!seqMappings.isEmpty())
2548       {
2549         break;
2550       }
2551     }
2552   
2553     if (sequence == null)
2554     {
2555       /*
2556        * No ungapped mapped sequence in middle column - do nothing
2557        */
2558       return 0;
2559     }
2560     MappingUtils.addSearchResults(sr, sequence,
2561             sequence.findPosition(middleColumn), mappings);
2562     return seqOffset;
2563   }
2564 }