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