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