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