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