JAL-1641 updated applet version
[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 java.awt.Color;
24 import java.util.ArrayDeque;
25 import java.util.ArrayList;
26 import java.util.BitSet;
27 import java.util.Deque;
28 import java.util.HashMap;
29 import java.util.Hashtable;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33
34 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
35 import jalview.analysis.Conservation;
36 import jalview.api.AlignCalcManagerI;
37 import jalview.api.AlignViewportI;
38 import jalview.api.AlignmentViewPanel;
39 import jalview.api.FeaturesDisplayedI;
40 import jalview.api.ViewStyleI;
41 import jalview.commands.CommandI;
42 import jalview.datamodel.AlignedCodonFrame;
43 import jalview.datamodel.AlignmentAnnotation;
44 import jalview.datamodel.AlignmentI;
45 import jalview.datamodel.AlignmentView;
46 import jalview.datamodel.Annotation;
47 import jalview.datamodel.CigarArray;
48 import jalview.datamodel.ColumnSelection;
49 import jalview.datamodel.HiddenSequences;
50 import jalview.datamodel.SearchResults;
51 import jalview.datamodel.Sequence;
52 import jalview.datamodel.SequenceCollectionI;
53 import jalview.datamodel.SequenceGroup;
54 import jalview.datamodel.SequenceI;
55 import jalview.schemes.Blosum62ColourScheme;
56 import jalview.schemes.ColourSchemeI;
57 import jalview.schemes.PIDColourScheme;
58 import jalview.schemes.ResidueProperties;
59 import jalview.structure.CommandListener;
60 import jalview.structure.StructureSelectionManager;
61 import jalview.structure.VamsasSource;
62 import jalview.util.Comparison;
63 import jalview.util.MappingUtils;
64 import jalview.viewmodel.styles.ViewStyle;
65 import jalview.workers.AlignCalcManager;
66 import jalview.workers.ComplementConsensusThread;
67 import jalview.workers.ConsensusThread;
68 import jalview.workers.StrucConsensusThread;
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   protected boolean hasHiddenRows = false;
1059
1060   @Override
1061   public boolean hasHiddenRows()
1062   {
1063     return hasHiddenRows;
1064   }
1065
1066   public void setHasHiddenRows(boolean hasHiddenRows)
1067   {
1068     this.hasHiddenRows = hasHiddenRows;
1069   }
1070
1071   protected SequenceGroup selectionGroup;
1072
1073   public void setSequenceSetId(String newid)
1074   {
1075     if (sequenceSetID != null)
1076     {
1077       System.err
1078               .println("Warning - overwriting a sequenceSetId for a viewport!");
1079     }
1080     sequenceSetID = new String(newid);
1081   }
1082
1083   @Override
1084   public String getSequenceSetId()
1085   {
1086     if (sequenceSetID == null)
1087     {
1088       sequenceSetID = alignment.hashCode() + "";
1089     }
1090
1091     return sequenceSetID;
1092   }
1093
1094   /**
1095    * unique viewId for synchronizing state (e.g. with stored Jalview Project)
1096    * 
1097    */
1098   protected String viewId = null;
1099
1100   @Override
1101   public String getViewId()
1102   {
1103     if (viewId == null)
1104     {
1105       viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
1106     }
1107     return viewId;
1108   }
1109
1110   public void setIgnoreGapsConsensus(boolean b, AlignmentViewPanel ap)
1111   {
1112     ignoreGapsInConsensusCalculation = b;
1113     if (ap != null)
1114     {
1115       updateConsensus(ap);
1116       if (globalColourScheme != null)
1117       {
1118         globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
1119                 ignoreGapsInConsensusCalculation);
1120       }
1121     }
1122
1123   }
1124
1125   private long sgrouphash = -1, colselhash = -1;
1126
1127   /**
1128    * checks current SelectionGroup against record of last hash value, and
1129    * updates record.
1130    * 
1131    * @param b
1132    *          update the record of last hash value
1133    * 
1134    * @return true if SelectionGroup changed since last call (when b is true)
1135    */
1136   public boolean isSelectionGroupChanged(boolean b)
1137   {
1138     int hc = (selectionGroup == null || selectionGroup.getSize() == 0) ? -1
1139             : selectionGroup.hashCode();
1140     if (hc != -1 && hc != sgrouphash)
1141     {
1142       if (b)
1143       {
1144         sgrouphash = hc;
1145       }
1146       return true;
1147     }
1148     return false;
1149   }
1150
1151   /**
1152    * checks current colsel against record of last hash value, and optionally
1153    * updates record.
1154    * 
1155    * @param b
1156    *          update the record of last hash value
1157    * @return true if colsel changed since last call (when b is true)
1158    */
1159   public boolean isColSelChanged(boolean b)
1160   {
1161     int hc = (colSel == null || colSel.size() == 0) ? -1 : colSel
1162             .hashCode();
1163     if (hc != -1 && hc != colselhash)
1164     {
1165       if (b)
1166       {
1167         colselhash = hc;
1168       }
1169       return true;
1170     }
1171     return false;
1172   }
1173
1174   @Override
1175   public boolean isIgnoreGapsConsensus()
1176   {
1177     return ignoreGapsInConsensusCalculation;
1178   }
1179
1180   // / property change stuff
1181
1182   // JBPNote Prolly only need this in the applet version.
1183   private final java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
1184           this);
1185
1186   protected boolean showConservation = true;
1187
1188   protected boolean showQuality = true;
1189
1190   protected boolean showConsensus = true;
1191
1192   private Map<SequenceI, Color> sequenceColours = new HashMap<SequenceI, Color>();
1193
1194   protected SequenceAnnotationOrder sortAnnotationsBy = null;
1195
1196   protected boolean showAutocalculatedAbove;
1197
1198   /**
1199    * when set, view will scroll to show the highlighted position
1200    */
1201   private boolean followHighlight = true;
1202
1203   // TODO private with getters and setters?
1204   public int startRes;
1205
1206   public int endRes;
1207
1208   public int startSeq;
1209
1210   public int endSeq;
1211
1212   /**
1213    * Property change listener for changes in alignment
1214    * 
1215    * @param listener
1216    *          DOCUMENT ME!
1217    */
1218   public void addPropertyChangeListener(
1219           java.beans.PropertyChangeListener listener)
1220   {
1221     changeSupport.addPropertyChangeListener(listener);
1222   }
1223
1224   /**
1225    * DOCUMENT ME!
1226    * 
1227    * @param listener
1228    *          DOCUMENT ME!
1229    */
1230   public void removePropertyChangeListener(
1231           java.beans.PropertyChangeListener listener)
1232   {
1233     changeSupport.removePropertyChangeListener(listener);
1234   }
1235
1236   /**
1237    * Property change listener for changes in alignment
1238    * 
1239    * @param prop
1240    *          DOCUMENT ME!
1241    * @param oldvalue
1242    *          DOCUMENT ME!
1243    * @param newvalue
1244    *          DOCUMENT ME!
1245    */
1246   public void firePropertyChange(String prop, Object oldvalue,
1247           Object newvalue)
1248   {
1249     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
1250   }
1251
1252   // common hide/show column stuff
1253
1254   public void hideSelectedColumns()
1255   {
1256     if (colSel.size() < 1)
1257     {
1258       return;
1259     }
1260
1261     colSel.hideSelectedColumns();
1262     setSelectionGroup(null);
1263
1264   }
1265
1266   public void hideColumns(int start, int end)
1267   {
1268     if (start == end)
1269     {
1270       colSel.hideColumns(start);
1271     }
1272     else
1273     {
1274       colSel.hideColumns(start, end);
1275     }
1276   }
1277
1278   public void showColumn(int col)
1279   {
1280     colSel.revealHiddenColumns(col);
1281
1282   }
1283
1284   public void showAllHiddenColumns()
1285   {
1286     colSel.revealAllHiddenColumns();
1287   }
1288
1289   // common hide/show seq stuff
1290   public void showAllHiddenSeqs()
1291   {
1292     if (alignment.getHiddenSequences().getSize() > 0)
1293     {
1294       if (selectionGroup == null)
1295       {
1296         selectionGroup = new SequenceGroup();
1297         selectionGroup.setEndRes(alignment.getWidth() - 1);
1298       }
1299       List<SequenceI> tmp = alignment.getHiddenSequences().showAll(
1300               hiddenRepSequences);
1301       for (SequenceI seq : tmp)
1302       {
1303         selectionGroup.addSequence(seq, false);
1304         setSequenceAnnotationsVisible(seq, true);
1305       }
1306
1307       hasHiddenRows = false;
1308       hiddenRepSequences = null;
1309
1310       firePropertyChange("alignment", null, alignment.getSequences());
1311       // used to set hasHiddenRows/hiddenRepSequences here, after the property
1312       // changed event
1313       sendSelection();
1314     }
1315   }
1316
1317   public void showSequence(int index)
1318   {
1319     List<SequenceI> tmp = alignment.getHiddenSequences().showSequence(
1320             index,
1321             hiddenRepSequences);
1322     if (tmp.size() > 0)
1323     {
1324       if (selectionGroup == null)
1325       {
1326         selectionGroup = new SequenceGroup();
1327         selectionGroup.setEndRes(alignment.getWidth() - 1);
1328       }
1329
1330       for (SequenceI seq : tmp)
1331       {
1332         selectionGroup.addSequence(seq, false);
1333         setSequenceAnnotationsVisible(seq, true);
1334       }
1335       // JBPNote: refactor: only update flag if we modified visiblity (used to
1336       // do this regardless)
1337       if (alignment.getHiddenSequences().getSize() < 1)
1338       {
1339         hasHiddenRows = false;
1340       }
1341       firePropertyChange("alignment", null, alignment.getSequences());
1342       sendSelection();
1343     }
1344   }
1345
1346   public void hideAllSelectedSeqs()
1347   {
1348     if (selectionGroup == null || selectionGroup.getSize() < 1)
1349     {
1350       return;
1351     }
1352
1353     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
1354
1355     hideSequence(seqs);
1356
1357     setSelectionGroup(null);
1358   }
1359
1360   public void hideSequence(SequenceI[] seq)
1361   {
1362     if (seq != null)
1363     {
1364       for (int i = 0; i < seq.length; i++)
1365       {
1366         alignment.getHiddenSequences().hideSequence(seq[i]);
1367         setSequenceAnnotationsVisible(seq[i], false);
1368       }
1369       hasHiddenRows = true;
1370       firePropertyChange("alignment", null, alignment.getSequences());
1371     }
1372   }
1373
1374   /**
1375    * Set visibility for any annotations for the given sequence.
1376    * 
1377    * @param sequenceI
1378    */
1379   protected void setSequenceAnnotationsVisible(SequenceI sequenceI,
1380           boolean visible)
1381   {
1382     for (AlignmentAnnotation ann : alignment.getAlignmentAnnotation())
1383     {
1384       if (ann.sequenceRef == sequenceI)
1385       {
1386         ann.visible = visible;
1387       }
1388     }
1389   }
1390
1391   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
1392   {
1393     int sSize = sg.getSize();
1394     if (sSize < 2)
1395     {
1396       return;
1397     }
1398
1399     if (hiddenRepSequences == null)
1400     {
1401       hiddenRepSequences = new Hashtable();
1402     }
1403
1404     hiddenRepSequences.put(repSequence, sg);
1405
1406     // Hide all sequences except the repSequence
1407     SequenceI[] seqs = new SequenceI[sSize - 1];
1408     int index = 0;
1409     for (int i = 0; i < sSize; i++)
1410     {
1411       if (sg.getSequenceAt(i) != repSequence)
1412       {
1413         if (index == sSize - 1)
1414         {
1415           return;
1416         }
1417
1418         seqs[index++] = sg.getSequenceAt(i);
1419       }
1420     }
1421     sg.setSeqrep(repSequence); // note: not done in 2.7applet
1422     sg.setHidereps(true); // note: not done in 2.7applet
1423     hideSequence(seqs);
1424
1425   }
1426
1427   public boolean isHiddenRepSequence(SequenceI seq)
1428   {
1429     return alignment.getSeqrep()==seq || (hiddenRepSequences != null
1430             && hiddenRepSequences.containsKey(seq));
1431   }
1432
1433   public SequenceGroup getRepresentedSequences(SequenceI seq)
1434   {
1435     return (SequenceGroup) (hiddenRepSequences == null ? null
1436             : hiddenRepSequences.get(seq));
1437   }
1438
1439   @Override
1440   public int adjustForHiddenSeqs(int alignmentIndex)
1441   {
1442     return alignment.getHiddenSequences().adjustForHiddenSeqs(
1443             alignmentIndex);
1444   }
1445
1446   @Override
1447   public void invertColumnSelection()
1448   {
1449     colSel.invertColumnSelection(0, alignment.getWidth());
1450   }
1451
1452
1453   @Override
1454   public SequenceI[] getSelectionAsNewSequence()
1455   {
1456     SequenceI[] sequences;
1457     // JBPNote: Need to test jalviewLite.getSelectedSequencesAsAlignmentFrom -
1458     // this was the only caller in the applet for this method
1459     // JBPNote: in applet, this method returned references to the alignment
1460     // sequences, and it did not honour the presence/absence of annotation
1461     // attached to the alignment (probably!)
1462     if (selectionGroup == null || selectionGroup.getSize() == 0)
1463     {
1464       sequences = alignment.getSequencesArray();
1465       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
1466       for (int i = 0; i < sequences.length; i++)
1467       {
1468         // construct new sequence with subset of visible annotation
1469         sequences[i] = new Sequence(sequences[i], annots);
1470       }
1471     }
1472     else
1473     {
1474       sequences = selectionGroup.getSelectionAsNewSequences(alignment);
1475     }
1476
1477     return sequences;
1478   }
1479
1480
1481   @Override
1482   public SequenceI[] getSequenceSelection()
1483   {
1484     SequenceI[] sequences = null;
1485     if (selectionGroup != null)
1486     {
1487       sequences = selectionGroup.getSequencesInOrder(alignment);
1488     }
1489     if (sequences == null)
1490     {
1491       sequences = alignment.getSequencesArray();
1492     }
1493     return sequences;
1494   }
1495
1496
1497   @Override
1498   public CigarArray getViewAsCigars(
1499           boolean selectedRegionOnly)
1500   {
1501     return new CigarArray(alignment, colSel,
1502             (selectedRegionOnly ? selectionGroup : null));
1503   }
1504
1505
1506   @Override
1507   public jalview.datamodel.AlignmentView getAlignmentView(
1508           boolean selectedOnly)
1509   {
1510     return getAlignmentView(selectedOnly, false);
1511   }
1512
1513
1514   @Override
1515   public jalview.datamodel.AlignmentView getAlignmentView(
1516           boolean selectedOnly, boolean markGroups)
1517   {
1518     return new AlignmentView(alignment, colSel, selectionGroup,
1519             colSel != null && colSel.hasHiddenColumns(), selectedOnly,
1520             markGroups);
1521   }
1522
1523
1524   @Override
1525   public String[] getViewAsString(boolean selectedRegionOnly)
1526   {
1527     String[] selection = null;
1528     SequenceI[] seqs = null;
1529     int i, iSize;
1530     int start = 0, end = 0;
1531     if (selectedRegionOnly && selectionGroup != null)
1532     {
1533       iSize = selectionGroup.getSize();
1534       seqs = selectionGroup.getSequencesInOrder(alignment);
1535       start = selectionGroup.getStartRes();
1536       end = selectionGroup.getEndRes() + 1;
1537     }
1538     else
1539     {
1540       iSize = alignment.getHeight();
1541       seqs = alignment.getSequencesArray();
1542       end = alignment.getWidth();
1543     }
1544
1545     selection = new String[iSize];
1546     if (colSel != null && colSel.hasHiddenColumns())
1547     {
1548       selection = colSel.getVisibleSequenceStrings(start, end, seqs);
1549     }
1550     else
1551     {
1552       for (i = 0; i < iSize; i++)
1553       {
1554         selection[i] = seqs[i].getSequenceAsString(start, end);
1555       }
1556
1557     }
1558     return selection;
1559   }
1560
1561
1562   @Override
1563   public List<int[]> getVisibleRegionBoundaries(int min, int max)
1564   {
1565     ArrayList<int[]> regions = new ArrayList<int[]>();
1566     int start = min;
1567     int end = max;
1568
1569     do
1570     {
1571       if (colSel != null && colSel.hasHiddenColumns())
1572       {
1573         if (start == 0)
1574         {
1575           start = colSel.adjustForHiddenColumns(start);
1576         }
1577
1578         end = colSel.getHiddenBoundaryRight(start);
1579         if (start == end)
1580         {
1581           end = max;
1582         }
1583         if (end > max)
1584         {
1585           end = max;
1586         }
1587       }
1588
1589       regions.add(new int[]
1590       { start, end });
1591
1592       if (colSel != null && colSel.hasHiddenColumns())
1593       {
1594         start = colSel.adjustForHiddenColumns(end);
1595         start = colSel.getHiddenBoundaryLeft(start) + 1;
1596       }
1597     } while (end < max);
1598
1599     int[][] startEnd = new int[regions.size()][2];
1600
1601     return regions;
1602   }
1603
1604   @Override
1605   public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(boolean selectedOnly)
1606   {
1607     ArrayList<AlignmentAnnotation> ala = new ArrayList<AlignmentAnnotation>();
1608     AlignmentAnnotation[] aa;
1609     if ((aa=alignment.getAlignmentAnnotation())!=null)
1610     {
1611       for (AlignmentAnnotation annot:aa)
1612       {
1613         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
1614         if (selectedOnly && selectionGroup!=null)
1615         {
1616           colSel.makeVisibleAnnotation(selectionGroup.getStartRes(), selectionGroup.getEndRes(),clone);
1617         } else {
1618           colSel.makeVisibleAnnotation(clone);
1619         }
1620         ala.add(clone);
1621       }
1622     }
1623     return ala;
1624   }
1625
1626
1627   @Override
1628   public boolean isPadGaps()
1629   {
1630     return padGaps;
1631   }
1632
1633
1634   @Override
1635   public void setPadGaps(boolean padGaps)
1636   {
1637     this.padGaps = padGaps;
1638   }
1639
1640   /**
1641    * apply any post-edit constraints and trigger any calculations needed after
1642    * an edit has been performed on the alignment
1643    * 
1644    * @param ap
1645    */
1646   @Override
1647   public void alignmentChanged(AlignmentViewPanel ap)
1648   {
1649     if (isPadGaps())
1650     {
1651       alignment.padGaps();
1652     }
1653     if (autoCalculateConsensus)
1654     {
1655       updateConsensus(ap);
1656     }
1657     if (hconsensus != null && autoCalculateConsensus)
1658     {
1659       updateConservation(ap);
1660     }
1661     if (autoCalculateStrucConsensus)
1662     {
1663       updateStrucConsensus(ap);
1664     }
1665
1666     // Reset endRes of groups if beyond alignment width
1667     int alWidth = alignment.getWidth();
1668     List<SequenceGroup> groups = alignment.getGroups();
1669     if (groups != null)
1670     {
1671       for (SequenceGroup sg : groups)
1672       {
1673         if (sg.getEndRes() > alWidth)
1674         {
1675           sg.setEndRes(alWidth - 1);
1676         }
1677       }
1678     }
1679
1680     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1681     {
1682       selectionGroup.setEndRes(alWidth - 1);
1683     }
1684
1685     resetAllColourSchemes();
1686     calculator.restartWorkers();
1687     // alignment.adjustSequenceAnnotations();
1688   }
1689
1690   /**
1691    * reset scope and do calculations for all applied colourschemes on alignment
1692    */
1693   void resetAllColourSchemes()
1694   {
1695     ColourSchemeI cs = globalColourScheme;
1696     if (cs != null)
1697     {
1698       cs.alignmentChanged(alignment, hiddenRepSequences);
1699
1700       cs.setConsensus(hconsensus);
1701       if (cs.conservationApplied())
1702       {
1703         cs.setConservation(Conservation.calculateConservation("All",
1704                 ResidueProperties.propHash, 3, alignment.getSequences(), 0,
1705                 alignment.getWidth(), false, getConsPercGaps(), false));
1706       }
1707     }
1708
1709     for (SequenceGroup sg : alignment.getGroups())
1710     {
1711       if (sg.cs != null)
1712       {
1713         sg.cs.alignmentChanged(sg, hiddenRepSequences);
1714       }
1715       sg.recalcConservation();
1716     }
1717   }
1718
1719   protected void initAutoAnnotation()
1720   {
1721     // TODO: add menu option action that nulls or creates consensus object
1722     // depending on if the user wants to see the annotation or not in a
1723     // specific alignment
1724
1725     if (hconsensus == null && !isDataset)
1726     {
1727       if (!alignment.isNucleotide())
1728       {
1729         initConservation();
1730         initQuality();
1731       }
1732       else
1733       {
1734         initRNAStructure();
1735       }
1736       consensus = new AlignmentAnnotation("Consensus", "PID",
1737               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
1738       initConsensus(consensus);
1739
1740       initComplementConsensus();
1741     }
1742   }
1743
1744   /**
1745    * If this is a protein alignment and there are mappings to cDNA, add the cDNA
1746    * consensus annotation.
1747    */
1748   public void initComplementConsensus()
1749   {
1750     if (!alignment.isNucleotide())
1751     {
1752       final Set<AlignedCodonFrame> codonMappings = alignment
1753               .getCodonFrames();
1754       if (codonMappings != null && !codonMappings.isEmpty())
1755       {
1756         complementConsensus = new AlignmentAnnotation("cDNA Consensus",
1757                 "PID for cDNA", new Annotation[1], 0f, 100f,
1758                 AlignmentAnnotation.BAR_GRAPH);
1759         initConsensus(complementConsensus);
1760       }
1761     }
1762   }
1763
1764   private void initConsensus(AlignmentAnnotation aa)
1765   {
1766     aa.hasText = true;
1767     aa.autoCalculated = true;
1768
1769     if (showConsensus)
1770     {
1771       alignment.addAnnotation(aa);
1772     }
1773   }
1774
1775   private void initConservation()
1776   {
1777     if (showConservation)
1778     {
1779       if (conservation == null)
1780       {
1781         conservation = new AlignmentAnnotation("Conservation",
1782                 "Conservation of total alignment less than "
1783                         + getConsPercGaps() + "% gaps", new Annotation[1],
1784                 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
1785         conservation.hasText = true;
1786         conservation.autoCalculated = true;
1787         alignment.addAnnotation(conservation);
1788       }
1789     }
1790   }
1791
1792   private void initQuality()
1793   {
1794     if (showQuality)
1795     {
1796       if (quality == null)
1797       {
1798         quality = new AlignmentAnnotation("Quality",
1799                 "Alignment Quality based on Blosum62 scores",
1800                 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
1801         quality.hasText = true;
1802         quality.autoCalculated = true;
1803         alignment.addAnnotation(quality);
1804       }
1805     }
1806   }
1807
1808   private void initRNAStructure()
1809   {
1810     if (alignment.hasRNAStructure() && strucConsensus == null)
1811     {
1812       strucConsensus = new AlignmentAnnotation("StrucConsensus", "PID",
1813               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
1814       strucConsensus.hasText = true;
1815       strucConsensus.autoCalculated = true;
1816
1817       if (showConsensus)
1818       {
1819         alignment.addAnnotation(strucConsensus);
1820       }
1821     }
1822   }
1823
1824   /*
1825    * (non-Javadoc)
1826    * 
1827    * @see jalview.api.AlignViewportI#calcPanelHeight()
1828    */
1829   @Override
1830   public int calcPanelHeight()
1831   {
1832     // setHeight of panels
1833     AlignmentAnnotation[] anns = getAlignment().getAlignmentAnnotation();
1834     int height = 0;
1835     int charHeight = getCharHeight();
1836     if (anns != null)
1837     {
1838       BitSet graphgrp = new BitSet();
1839       for (AlignmentAnnotation aa : anns)
1840       {
1841         if (aa == null)
1842         {
1843           System.err.println("Null annotation row: ignoring.");
1844           continue;
1845         }
1846         if (!aa.visible)
1847         {
1848           continue;
1849         }
1850         if (aa.graphGroup > -1)
1851         {
1852           if (graphgrp.get(aa.graphGroup))
1853           {
1854             continue;
1855           }
1856           else
1857           {
1858             graphgrp.set(aa.graphGroup);
1859           }
1860         }
1861         aa.height = 0;
1862
1863         if (aa.hasText)
1864         {
1865           aa.height += charHeight;
1866         }
1867
1868         if (aa.hasIcons)
1869         {
1870           aa.height += 16;
1871         }
1872
1873         if (aa.graph > 0)
1874         {
1875           aa.height += aa.graphHeight;
1876         }
1877
1878         if (aa.height == 0)
1879         {
1880           aa.height = 20;
1881         }
1882
1883         height += aa.height;
1884       }
1885     }
1886     if (height == 0)
1887     {
1888       // set minimum
1889       height = 20;
1890     }
1891     return height;
1892   }
1893
1894   @Override
1895   public void updateGroupAnnotationSettings(boolean applyGlobalSettings,
1896           boolean preserveNewGroupSettings)
1897   {
1898     boolean updateCalcs = false;
1899     boolean conv = isShowGroupConservation();
1900     boolean cons = isShowGroupConsensus();
1901     boolean showprf = isShowSequenceLogo();
1902     boolean showConsHist = isShowConsensusHistogram();
1903     boolean normLogo = isNormaliseSequenceLogo();
1904
1905     /**
1906      * TODO reorder the annotation rows according to group/sequence ordering on
1907      * alignment
1908      */
1909     boolean sortg = true;
1910
1911     // remove old automatic annotation
1912     // add any new annotation
1913
1914     // intersect alignment annotation with alignment groups
1915
1916     AlignmentAnnotation[] aan = alignment.getAlignmentAnnotation();
1917     List<SequenceGroup> oldrfs = new ArrayList<SequenceGroup>();
1918     if (aan != null)
1919     {
1920       for (int an = 0; an < aan.length; an++)
1921       {
1922         if (aan[an].autoCalculated && aan[an].groupRef != null)
1923         {
1924           oldrfs.add(aan[an].groupRef);
1925           alignment.deleteAnnotation(aan[an], false);
1926         }
1927       }
1928     }
1929     if (alignment.getGroups() != null)
1930     {
1931       for (SequenceGroup sg : alignment.getGroups())
1932       {
1933         updateCalcs = false;
1934         if (applyGlobalSettings
1935                 || (!preserveNewGroupSettings && !oldrfs.contains(sg)))
1936         {
1937           // set defaults for this group's conservation/consensus
1938           sg.setshowSequenceLogo(showprf);
1939           sg.setShowConsensusHistogram(showConsHist);
1940           sg.setNormaliseSequenceLogo(normLogo);
1941         }
1942         if (conv)
1943         {
1944           updateCalcs = true;
1945           alignment.addAnnotation(sg.getConservationRow(), 0);
1946         }
1947         if (cons)
1948         {
1949           updateCalcs = true;
1950           alignment.addAnnotation(sg.getConsensus(), 0);
1951         }
1952         // refresh the annotation rows
1953         if (updateCalcs)
1954         {
1955           sg.recalcConservation();
1956         }
1957       }
1958     }
1959     oldrfs.clear();
1960   }
1961   @Override
1962   public boolean isDisplayReferenceSeq()
1963   {
1964     return alignment.hasSeqrep() && viewStyle.isDisplayReferenceSeq();
1965   }
1966
1967   @Override
1968   public void setDisplayReferenceSeq(boolean displayReferenceSeq)
1969   {
1970     viewStyle.setDisplayReferenceSeq(displayReferenceSeq);
1971   }
1972
1973   @Override
1974   public boolean isColourByReferenceSeq()
1975   {
1976     return alignment.hasSeqrep() && viewStyle.isColourByReferenceSeq();
1977   }
1978
1979   @Override
1980   public Color getSequenceColour(SequenceI seq)
1981   {
1982     Color sqc = sequenceColours.get(seq);
1983     return (sqc == null ? Color.white : sqc);
1984   }
1985
1986   @Override
1987   public void setSequenceColour(SequenceI seq, Color col)
1988   {
1989     if (col == null)
1990     {
1991       sequenceColours.remove(seq);
1992     }
1993     else
1994     {
1995       sequenceColours.put(seq, col);
1996     }
1997   }
1998
1999   @Override
2000   public void updateSequenceIdColours()
2001   {
2002     for (SequenceGroup sg : alignment.getGroups())
2003     {
2004       if (sg.idColour != null)
2005       {
2006         for (SequenceI s : sg.getSequences(getHiddenRepSequences()))
2007         {
2008           sequenceColours.put(s, sg.idColour);
2009         }
2010       }
2011     }
2012   }
2013
2014   @Override
2015   public void clearSequenceColours()
2016   {
2017     sequenceColours.clear();
2018   };
2019
2020   @Override
2021   public AlignViewportI getCodingComplement()
2022   {
2023     return this.codingComplement;
2024   }
2025
2026   /**
2027    * Set this as the (cDna/protein) complement of the given viewport. Also
2028    * ensures the reverse relationship is set on the given viewport.
2029    */
2030   @Override
2031   public void setCodingComplement(AlignViewportI av)
2032   {
2033     if (this == av)
2034     {
2035       System.err.println("Ignoring recursive setCodingComplement request");
2036     }
2037     else
2038     {
2039       this.codingComplement = av;
2040       // avoid infinite recursion!
2041       if (av.getCodingComplement() != this)
2042       {
2043         av.setCodingComplement(this);
2044       }
2045     }
2046   }
2047
2048   @Override
2049   public boolean isNucleotide()
2050   {
2051     return getAlignment() == null ? false : getAlignment().isNucleotide();
2052   }
2053
2054   @Override
2055   public FeaturesDisplayedI getFeaturesDisplayed()
2056   {
2057     return featuresDisplayed;
2058   }
2059
2060   @Override
2061   public void setFeaturesDisplayed(FeaturesDisplayedI featuresDisplayedI)
2062   {
2063     featuresDisplayed = featuresDisplayedI;
2064   }
2065
2066   @Override
2067   public boolean areFeaturesDisplayed()
2068   {
2069     return featuresDisplayed != null && featuresDisplayed.getRegisterdFeaturesCount()>0;
2070   }
2071
2072   /**
2073    * set the flag
2074    * 
2075    * @param b
2076    *          features are displayed if true
2077    */
2078   @Override
2079   public void setShowSequenceFeatures(boolean b)
2080   {
2081     viewStyle.setShowSequenceFeatures(b);
2082   }
2083   @Override
2084   public boolean isShowSequenceFeatures()
2085   {
2086     return viewStyle.isShowSequenceFeatures();
2087   }
2088
2089   @Override
2090   public void setShowSequenceFeaturesHeight(boolean selected)
2091   {
2092     viewStyle.setShowSequenceFeaturesHeight(selected);
2093   }
2094
2095   @Override
2096   public boolean isShowSequenceFeaturesHeight()
2097   {
2098     return viewStyle.isShowSequenceFeaturesHeight();
2099   }
2100
2101
2102
2103   @Override
2104   public void setShowAnnotation(boolean b)
2105   {
2106     viewStyle.setShowAnnotation(b);
2107   }
2108
2109   @Override
2110   public boolean isShowAnnotation()
2111   {
2112     return viewStyle.isShowAnnotation();
2113   }
2114
2115   @Override
2116   public boolean isRightAlignIds()
2117   {
2118     return viewStyle.isRightAlignIds();
2119   }
2120
2121   @Override
2122   public void setRightAlignIds(boolean rightAlignIds)
2123   {
2124     viewStyle.setRightAlignIds(rightAlignIds);
2125   }
2126
2127   @Override
2128   public boolean getConservationSelected()
2129   {
2130     return viewStyle.getConservationSelected();
2131   }
2132
2133   @Override
2134   public void setShowBoxes(boolean state)
2135   {
2136     viewStyle.setShowBoxes(state);
2137   }
2138
2139   /**
2140    * @return
2141    * @see jalview.api.ViewStyleI#getTextColour()
2142    */
2143   public Color getTextColour()
2144   {
2145     return viewStyle.getTextColour();
2146   }
2147
2148   /**
2149    * @return
2150    * @see jalview.api.ViewStyleI#getTextColour2()
2151    */
2152   public Color getTextColour2()
2153   {
2154     return viewStyle.getTextColour2();
2155   }
2156
2157   /**
2158    * @return
2159    * @see jalview.api.ViewStyleI#getThresholdTextColour()
2160    */
2161   public int getThresholdTextColour()
2162   {
2163     return viewStyle.getThresholdTextColour();
2164   }
2165
2166   /**
2167    * @return
2168    * @see jalview.api.ViewStyleI#isConservationColourSelected()
2169    */
2170   public boolean isConservationColourSelected()
2171   {
2172     return viewStyle.isConservationColourSelected();
2173   }
2174
2175   /**
2176    * @return
2177    * @see jalview.api.ViewStyleI#isRenderGaps()
2178    */
2179   public boolean isRenderGaps()
2180   {
2181     return viewStyle.isRenderGaps();
2182   }
2183
2184   /**
2185    * @return
2186    * @see jalview.api.ViewStyleI#isShowColourText()
2187    */
2188   public boolean isShowColourText()
2189   {
2190     return viewStyle.isShowColourText();
2191   }
2192
2193   /**
2194    * @param conservationColourSelected
2195    * @see jalview.api.ViewStyleI#setConservationColourSelected(boolean)
2196    */
2197   public void setConservationColourSelected(
2198           boolean conservationColourSelected)
2199   {
2200     viewStyle.setConservationColourSelected(conservationColourSelected);
2201   }
2202
2203   /**
2204    * @param showColourText
2205    * @see jalview.api.ViewStyleI#setShowColourText(boolean)
2206    */
2207   public void setShowColourText(boolean showColourText)
2208   {
2209     viewStyle.setShowColourText(showColourText);
2210   }
2211
2212   /**
2213    * @param textColour
2214    * @see jalview.api.ViewStyleI#setTextColour(java.awt.Color)
2215    */
2216   public void setTextColour(Color textColour)
2217   {
2218     viewStyle.setTextColour(textColour);
2219   }
2220
2221   /**
2222    * @param thresholdTextColour
2223    * @see jalview.api.ViewStyleI#setThresholdTextColour(int)
2224    */
2225   public void setThresholdTextColour(int thresholdTextColour)
2226   {
2227     viewStyle.setThresholdTextColour(thresholdTextColour);
2228   }
2229
2230   /**
2231    * @param textColour2
2232    * @see jalview.api.ViewStyleI#setTextColour2(java.awt.Color)
2233    */
2234   public void setTextColour2(Color textColour2)
2235   {
2236     viewStyle.setTextColour2(textColour2);
2237   }
2238
2239   @Override
2240   public ViewStyleI getViewStyle()
2241   {
2242     return new ViewStyle(viewStyle);
2243   }
2244
2245   @Override
2246   public void setViewStyle(ViewStyleI settingsForView)
2247   {
2248     viewStyle = new ViewStyle(settingsForView);
2249   }
2250
2251   @Override
2252   public boolean sameStyle(ViewStyleI them)
2253   {
2254     return viewStyle.sameStyle(them);
2255   }
2256
2257   /**
2258    * @return
2259    * @see jalview.api.ViewStyleI#getIdWidth()
2260    */
2261   public int getIdWidth()
2262   {
2263     return viewStyle.getIdWidth();
2264   }
2265
2266   /**
2267    * @param i
2268    * @see jalview.api.ViewStyleI#setIdWidth(int)
2269    */
2270   public void setIdWidth(int i)
2271   {
2272     viewStyle.setIdWidth(i);
2273   }
2274
2275   /**
2276    * @return
2277    * @see jalview.api.ViewStyleI#isCentreColumnLabels()
2278    */
2279   public boolean isCentreColumnLabels()
2280   {
2281     return viewStyle.isCentreColumnLabels();
2282   }
2283
2284   /**
2285    * @param centreColumnLabels
2286    * @see jalview.api.ViewStyleI#setCentreColumnLabels(boolean)
2287    */
2288   public void setCentreColumnLabels(boolean centreColumnLabels)
2289   {
2290     viewStyle.setCentreColumnLabels(centreColumnLabels);
2291   }
2292
2293   /**
2294    * @param showdbrefs
2295    * @see jalview.api.ViewStyleI#setShowDBRefs(boolean)
2296    */
2297   public void setShowDBRefs(boolean showdbrefs)
2298   {
2299     viewStyle.setShowDBRefs(showdbrefs);
2300   }
2301
2302   /**
2303    * @return
2304    * @see jalview.api.ViewStyleI#isShowDBRefs()
2305    */
2306   public boolean isShowDBRefs()
2307   {
2308     return viewStyle.isShowDBRefs();
2309   }
2310
2311   /**
2312    * @return
2313    * @see jalview.api.ViewStyleI#isShowNPFeats()
2314    */
2315   public boolean isShowNPFeats()
2316   {
2317     return viewStyle.isShowNPFeats();
2318   }
2319
2320   /**
2321    * @param shownpfeats
2322    * @see jalview.api.ViewStyleI#setShowNPFeats(boolean)
2323    */
2324   public void setShowNPFeats(boolean shownpfeats)
2325   {
2326     viewStyle.setShowNPFeats(shownpfeats);
2327   }
2328
2329   public abstract StructureSelectionManager getStructureSelectionManager();
2330
2331   /**
2332    * Add one command to the command history list.
2333    * 
2334    * @param command
2335    */
2336   public void addToHistoryList(CommandI command)
2337   {
2338     if (this.historyList != null)
2339     {
2340       this.historyList.push(command);
2341       broadcastCommand(command, false);
2342     }
2343   }
2344
2345   protected void broadcastCommand(CommandI command, boolean undo)
2346   {
2347     getStructureSelectionManager().commandPerformed(command, undo, getVamsasSource());
2348   }
2349
2350   /**
2351    * Add one command to the command redo list.
2352    * 
2353    * @param command
2354    */
2355   public void addToRedoList(CommandI command)
2356   {
2357     if (this.redoList != null)
2358     {
2359       this.redoList.push(command);
2360     }
2361     broadcastCommand(command, true);
2362   }
2363
2364   /**
2365    * Clear the command redo list.
2366    */
2367   public void clearRedoList()
2368   {
2369     if (this.redoList != null)
2370     {
2371       this.redoList.clear();
2372     }
2373   }
2374
2375   public void setHistoryList(Deque<CommandI> list)
2376   {
2377     this.historyList = list;
2378   }
2379
2380   public Deque<CommandI> getHistoryList()
2381   {
2382     return this.historyList;
2383   }
2384
2385   public void setRedoList(Deque<CommandI> list)
2386   {
2387     this.redoList = list;
2388   }
2389
2390   public Deque<CommandI> getRedoList()
2391   {
2392     return this.redoList;
2393   }
2394
2395   @Override
2396   public VamsasSource getVamsasSource()
2397   {
2398     return this;
2399   }
2400
2401   public SequenceAnnotationOrder getSortAnnotationsBy()
2402   {
2403     return sortAnnotationsBy;
2404   }
2405
2406   public void setSortAnnotationsBy(SequenceAnnotationOrder sortAnnotationsBy)
2407   {
2408     this.sortAnnotationsBy = sortAnnotationsBy;
2409   }
2410
2411   public boolean isShowAutocalculatedAbove()
2412   {
2413     return showAutocalculatedAbove;
2414   }
2415
2416   public void setShowAutocalculatedAbove(boolean showAutocalculatedAbove)
2417   {
2418     this.showAutocalculatedAbove = showAutocalculatedAbove;
2419   }
2420
2421   @Override
2422   public boolean isScaleProteinAsCdna()
2423   {
2424     return viewStyle.isScaleProteinAsCdna();
2425   }
2426
2427   @Override
2428   public void setScaleProteinAsCdna(boolean b)
2429   {
2430     viewStyle.setScaleProteinAsCdna(b);
2431   }
2432
2433   /**
2434    * @return true if view should scroll to show the highlighted region of a
2435    *         sequence
2436    * @return
2437    */
2438   @Override
2439   public final boolean isFollowHighlight()
2440   {
2441     return followHighlight;
2442   }
2443
2444   @Override
2445   public final void setFollowHighlight(boolean b)
2446   {
2447     this.followHighlight = b;
2448   }
2449
2450   public int getStartRes()
2451   {
2452     return startRes;
2453   }
2454
2455   public int getEndRes()
2456   {
2457     return endRes;
2458   }
2459
2460   public int getStartSeq()
2461   {
2462     return startSeq;
2463   }
2464
2465   public void setStartRes(int res)
2466   {
2467     this.startRes = res;
2468   }
2469
2470   public void setStartSeq(int seq)
2471   {
2472     this.startSeq = seq;
2473   }
2474
2475   public void setEndRes(int res)
2476   {
2477     if (res > alignment.getWidth() - 1)
2478     {
2479       // log.System.out.println(" Corrected res from " + res + " to maximum " +
2480       // (alignment.getWidth()-1));
2481       res = alignment.getWidth() - 1;
2482     }
2483     if (res < 0)
2484     {
2485       res = 0;
2486     }
2487     this.endRes = res;
2488   }
2489
2490   public void setEndSeq(int seq)
2491   {
2492     if (seq > alignment.getHeight())
2493     {
2494       seq = alignment.getHeight();
2495     }
2496     if (seq < 0)
2497     {
2498       seq = 0;
2499     }
2500     this.endSeq = seq;
2501   }
2502
2503   public int getEndSeq()
2504   {
2505     return endSeq;
2506   }
2507
2508   /**
2509    * Helper method to populate the SearchResults with the location in the
2510    * complementary alignment to scroll to, in order to match this one.
2511    * 
2512    * @param sr
2513    *          the SearchResults to add to
2514    * @return the offset (below top of visible region) of the matched sequence
2515    */
2516   protected int findComplementScrollTarget(SearchResults sr)
2517   {
2518     final AlignViewportI codingComplement = getCodingComplement();
2519     if (codingComplement == null || !codingComplement.isFollowHighlight())
2520     {
2521       return 0;
2522     }
2523     boolean iAmProtein = !getAlignment().isNucleotide();
2524     AlignmentI proteinAlignment = iAmProtein ? getAlignment()
2525             : codingComplement.getAlignment();
2526     if (proteinAlignment == null)
2527     {
2528       return 0;
2529     }
2530     final Set<AlignedCodonFrame> mappings = proteinAlignment
2531             .getCodonFrames();
2532   
2533     /*
2534      * Heuristic: find the first mapped sequence (if any) with a non-gapped
2535      * residue in the middle column of the visible region. Scroll the
2536      * complementary alignment to line up the corresponding residue.
2537      */
2538     int seqOffset = 0;
2539     SequenceI sequence = null;
2540
2541     /*
2542      * locate 'middle' column (true middle if an odd number visible, left of
2543      * middle if an even number visible)
2544      */
2545     int middleColumn = getStartRes() + (getEndRes() - getStartRes()) / 2;
2546     final HiddenSequences hiddenSequences = getAlignment()
2547             .getHiddenSequences();
2548     for (int seqNo = getStartSeq(); seqNo < getEndSeq(); seqNo++, seqOffset++)
2549     {
2550       sequence = getAlignment().getSequenceAt(seqNo);
2551       if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
2552       {
2553         continue;
2554       }
2555       if (Comparison.isGap(sequence.getCharAt(middleColumn)))
2556       {
2557         continue;
2558       }
2559       List<AlignedCodonFrame> seqMappings = MappingUtils
2560               .findMappingsForSequence(sequence, mappings);
2561       if (!seqMappings.isEmpty())
2562       {
2563         break;
2564       }
2565     }
2566   
2567     if (sequence == null)
2568     {
2569       /*
2570        * No ungapped mapped sequence in middle column - do nothing
2571        */
2572       return 0;
2573     }
2574     MappingUtils.addSearchResults(sr, sequence,
2575             sequence.findPosition(middleColumn), mappings);
2576     return seqOffset;
2577   }
2578 }