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