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