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