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