JAL - 3690 AlignCalc rebuilt - FutureTask-based manager
[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 = null;
1004     residueShading = null; // may hold a reference to Consensus
1005     changeSupport = null;
1006     ranges = null;
1007     currentTree = null;
1008     selectionGroup = null;
1009     setAlignment(null);
1010   }
1011
1012   @Override
1013   public boolean isClosed()
1014   {
1015     // TODO: check that this isClosed is only true after panel is closed, not
1016     // before it is fully constructed.
1017     return alignment == null;
1018   }
1019
1020   @Override
1021   public AlignCalcManagerI2 getCalcManager()
1022   {
1023     return calculator;
1024   }
1025
1026   /**
1027    * should conservation rows be shown for groups
1028    */
1029   protected boolean showGroupConservation = false;
1030
1031   /**
1032    * should consensus rows be shown for groups
1033    */
1034   protected boolean showGroupConsensus = false;
1035
1036   /**
1037    * should consensus profile be rendered by default
1038    */
1039   protected boolean showSequenceLogo = false;
1040
1041   /**
1042    * should consensus profile be rendered normalised to row height
1043    */
1044   protected boolean normaliseSequenceLogo = false;
1045
1046   /**
1047    * should consensus histograms be rendered by default
1048    */
1049   protected boolean showConsensusHistogram = true;
1050
1051   /**
1052    * should hmm profile be rendered by default
1053    */
1054   protected boolean hmmShowSequenceLogo = false;
1055
1056   /**
1057    * should hmm profile be rendered normalised to row height
1058    */
1059   protected boolean hmmNormaliseSequenceLogo = false;
1060
1061   /**
1062    * should information histograms be rendered by default
1063    */
1064   protected boolean hmmShowHistogram = true;
1065
1066   /**
1067    * @return the showConsensusProfile
1068    */
1069   @Override
1070   public boolean isShowSequenceLogo()
1071   {
1072     return showSequenceLogo;
1073   }
1074
1075   /**
1076    * @return the showInformationProfile
1077    */
1078   @Override
1079   public boolean isShowHMMSequenceLogo()
1080   {
1081     return hmmShowSequenceLogo;
1082   }
1083
1084   /**
1085    * @param showSequenceLogo
1086    *          the new value
1087    */
1088   public void setShowSequenceLogo(boolean showSequenceLogo)
1089   {
1090     if (showSequenceLogo != this.showSequenceLogo)
1091     {
1092       // TODO: decouple settings setting from calculation when refactoring
1093       // annotation update method from alignframe to viewport
1094       this.showSequenceLogo = showSequenceLogo;
1095       for (AlignCalcWorkerI worker : calculator.getWorkers())
1096       {
1097         if (worker.getClass().equals(ConsensusThread.class) ||
1098                 worker.getClass().equals(ComplementConsensusThread.class) ||
1099                 worker.getClass().equals(StrucConsensusThread.class))
1100         {
1101           worker.updateAnnotation();
1102         }
1103       }
1104     }
1105     this.showSequenceLogo = showSequenceLogo;
1106   }
1107
1108   public void setShowHMMSequenceLogo(boolean showHMMSequenceLogo)
1109   {
1110     if (showHMMSequenceLogo != this.hmmShowSequenceLogo)
1111     {
1112       this.hmmShowSequenceLogo = showHMMSequenceLogo;
1113       // TODO: updateAnnotation if description (tooltip) will show
1114       // profile in place of information content?
1115       // calculator.updateAnnotationFor(InformationThread.class);
1116     }
1117     this.hmmShowSequenceLogo = showHMMSequenceLogo;
1118   }
1119
1120   /**
1121    * @param showConsensusHistogram
1122    *          the showConsensusHistogram to set
1123    */
1124   public void setShowConsensusHistogram(boolean showConsensusHistogram)
1125   {
1126     this.showConsensusHistogram = showConsensusHistogram;
1127   }
1128
1129   /**
1130    * @param showInformationHistogram
1131    */
1132   public void setShowInformationHistogram(boolean showInformationHistogram)
1133   {
1134     this.hmmShowHistogram = showInformationHistogram;
1135   }
1136
1137   /**
1138    * @return the showGroupConservation
1139    */
1140   public boolean isShowGroupConservation()
1141   {
1142     return showGroupConservation;
1143   }
1144
1145   /**
1146    * @param showGroupConservation
1147    *          the showGroupConservation to set
1148    */
1149   public void setShowGroupConservation(boolean showGroupConservation)
1150   {
1151     this.showGroupConservation = showGroupConservation;
1152   }
1153
1154   /**
1155    * @return the showGroupConsensus
1156    */
1157   public boolean isShowGroupConsensus()
1158   {
1159     return showGroupConsensus;
1160   }
1161
1162   /**
1163    * @param showGroupConsensus
1164    *          the showGroupConsensus to set
1165    */
1166   public void setShowGroupConsensus(boolean showGroupConsensus)
1167   {
1168     this.showGroupConsensus = showGroupConsensus;
1169   }
1170
1171   /**
1172    * 
1173    * @return flag to indicate if the consensus histogram should be rendered by
1174    *         default
1175    */
1176   @Override
1177   public boolean isShowConsensusHistogram()
1178   {
1179     return this.showConsensusHistogram;
1180   }
1181
1182   /**
1183    * 
1184    * @return flag to indicate if the information content histogram should be
1185    *         rendered by default
1186    */
1187   @Override
1188   public boolean isShowInformationHistogram()
1189   {
1190     return this.hmmShowHistogram;
1191   }
1192
1193   /**
1194    * when set, updateAlignment will always ensure sequences are of equal length
1195    */
1196   private boolean padGaps = false;
1197
1198   /**
1199    * when set, alignment should be reordered according to a newly opened tree
1200    */
1201   public boolean sortByTree = false;
1202
1203   /**
1204    * 
1205    * 
1206    * @return null or the currently selected sequence region
1207    */
1208   @Override
1209   public SequenceGroup getSelectionGroup()
1210   {
1211     return selectionGroup;
1212   }
1213
1214   /**
1215    * Set the selection group for this window. Also sets the current alignment as
1216    * the context for the group, if it does not already have one.
1217    * 
1218    * @param sg
1219    *          - group holding references to sequences in this alignment view
1220    * 
1221    */
1222   @Override
1223   public void setSelectionGroup(SequenceGroup sg)
1224   {
1225     selectionGroup = sg;
1226     if (sg != null && sg.getContext() == null)
1227     {
1228       sg.setContext(alignment);
1229     }
1230   }
1231
1232   public void setHiddenColumns(HiddenColumns hidden)
1233   {
1234     this.alignment.setHiddenColumns(hidden);
1235   }
1236
1237   @Override
1238   public ColumnSelection getColumnSelection()
1239   {
1240     return colSel;
1241   }
1242
1243   @Override
1244   public void setColumnSelection(ColumnSelection colSel)
1245   {
1246     this.colSel = colSel;
1247     if (colSel != null)
1248     {
1249       updateHiddenColumns();
1250     }
1251     isColSelChanged(true);
1252   }
1253
1254   /**
1255    * 
1256    * @return
1257    */
1258   @Override
1259   public Map<SequenceI, SequenceCollectionI> getHiddenRepSequences()
1260   {
1261     return hiddenRepSequences;
1262   }
1263
1264   @Override
1265   public void setHiddenRepSequences(
1266           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
1267   {
1268     this.hiddenRepSequences = hiddenRepSequences;
1269   }
1270
1271   @Override
1272   public boolean hasSelectedColumns()
1273   {
1274     ColumnSelection columnSelection = getColumnSelection();
1275     return columnSelection != null && columnSelection.hasSelectedColumns();
1276   }
1277
1278   @Override
1279   public boolean hasHiddenColumns()
1280   {
1281     return alignment.getHiddenColumns() != null
1282             && alignment.getHiddenColumns().hasHiddenColumns();
1283   }
1284
1285   public void updateHiddenColumns()
1286   {
1287     // this method doesn't really do anything now. But - it could, since a
1288     // column Selection could be in the process of modification
1289     // hasHiddenColumns = colSel.hasHiddenColumns();
1290   }
1291
1292   @Override
1293   public boolean hasHiddenRows()
1294   {
1295     return alignment.getHiddenSequences().getSize() > 0;
1296   }
1297
1298   protected SequenceGroup selectionGroup;
1299
1300   public void setSequenceSetId(String newid)
1301   {
1302     if (sequenceSetID != null)
1303     {
1304       System.err.println(
1305               "Warning - overwriting a sequenceSetId for a viewport!");
1306     }
1307     sequenceSetID = new String(newid);
1308   }
1309
1310   @Override
1311   public String getSequenceSetId()
1312   {
1313     if (sequenceSetID == null)
1314     {
1315       sequenceSetID = alignment.hashCode() + "";
1316     }
1317
1318     return sequenceSetID;
1319   }
1320
1321   /**
1322    * unique viewId for synchronizing state (e.g. with stored Jalview Project)
1323    * 
1324    */
1325   protected String viewId = null;
1326
1327   @Override
1328   public String getViewId()
1329   {
1330     if (viewId == null)
1331     {
1332       viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
1333     }
1334     return viewId;
1335   }
1336
1337   public void setIgnoreGapsConsensus(boolean b, AlignmentViewPanel ap)
1338   {
1339     ignoreGapsInConsensusCalculation = b;
1340     if (ap != null)
1341     {
1342       updateConsensus(ap);
1343       if (residueShading != null)
1344       {
1345         residueShading.setThreshold(residueShading.getThreshold(),
1346                 ignoreGapsInConsensusCalculation);
1347       }
1348     }
1349   }
1350
1351   public void setIgnoreBelowBackground(boolean b, AlignmentViewPanel ap)
1352   {
1353     ignoreBelowBackGroundFrequencyCalculation = b;
1354   }
1355
1356   public void setInfoLetterHeight(boolean b, AlignmentViewPanel ap)
1357   {
1358     infoLetterHeight = b;
1359   }
1360
1361   private long sgrouphash = -1, colselhash = -1;
1362
1363   /**
1364    * checks current SelectionGroup against record of last hash value, and
1365    * updates record.
1366    * 
1367    * @param b
1368    *          update the record of last hash value
1369    * 
1370    * @return true if SelectionGroup changed since last call (when b is true)
1371    */
1372   public boolean isSelectionGroupChanged(boolean b)
1373   {
1374     int hc = (selectionGroup == null || selectionGroup.getSize() == 0) ? -1
1375             : selectionGroup.hashCode();
1376     if (hc != -1 && hc != sgrouphash)
1377     {
1378       if (b)
1379       {
1380         sgrouphash = hc;
1381       }
1382       return true;
1383     }
1384     return false;
1385   }
1386
1387   /**
1388    * checks current colsel against record of last hash value, and optionally
1389    * updates record.
1390    * 
1391    * @param b
1392    *          update the record of last hash value
1393    * @return true if colsel changed since last call (when b is true)
1394    */
1395   public boolean isColSelChanged(boolean b)
1396   {
1397     int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel.hashCode();
1398     if (hc != -1 && hc != colselhash)
1399     {
1400       if (b)
1401       {
1402         colselhash = hc;
1403       }
1404       return true;
1405     }
1406     return false;
1407   }
1408
1409   @Override
1410   public boolean isIgnoreGapsConsensus()
1411   {
1412     return ignoreGapsInConsensusCalculation;
1413   }
1414
1415   @Override
1416   public boolean isIgnoreBelowBackground()
1417   {
1418     return ignoreBelowBackGroundFrequencyCalculation;
1419   }
1420
1421   @Override
1422   public boolean isInfoLetterHeight()
1423   {
1424     return infoLetterHeight;
1425   }
1426
1427   // property change stuff
1428   // JBPNote Prolly only need this in the applet version.
1429   private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
1430           this);
1431
1432   protected boolean showConservation = true;
1433
1434   protected boolean showQuality = true;
1435
1436   protected boolean showConsensus = true;
1437
1438   protected boolean showOccupancy = true;
1439
1440   private Map<SequenceI, Color> sequenceColours = new HashMap<>();
1441
1442   protected SequenceAnnotationOrder sortAnnotationsBy = null;
1443
1444   protected boolean showAutocalculatedAbove;
1445
1446   /**
1447    * when set, view will scroll to show the highlighted position
1448    */
1449   private boolean followHighlight = true;
1450
1451   /**
1452    * Property change listener for changes in alignment
1453    * 
1454    * @param listener
1455    *          DOCUMENT ME!
1456    */
1457   public void addPropertyChangeListener(
1458           java.beans.PropertyChangeListener listener)
1459   {
1460     changeSupport.addPropertyChangeListener(listener);
1461   }
1462
1463   /**
1464    * DOCUMENT ME!
1465    * 
1466    * @param listener
1467    *          DOCUMENT ME!
1468    */
1469   public void removePropertyChangeListener(
1470           java.beans.PropertyChangeListener listener)
1471   {
1472     if (changeSupport != null)
1473     {
1474       changeSupport.removePropertyChangeListener(listener);
1475     }
1476   }
1477
1478   /**
1479    * Property change listener for changes in alignment
1480    * 
1481    * @param prop
1482    *          DOCUMENT ME!
1483    * @param oldvalue
1484    *          DOCUMENT ME!
1485    * @param newvalue
1486    *          DOCUMENT ME!
1487    */
1488   public void firePropertyChange(String prop, Object oldvalue,
1489           Object newvalue)
1490   {
1491     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
1492   }
1493
1494   // common hide/show column stuff
1495
1496   public void hideSelectedColumns()
1497   {
1498     if (colSel.isEmpty())
1499     {
1500       return;
1501     }
1502
1503     colSel.hideSelectedColumns(alignment);
1504     setSelectionGroup(null);
1505     isColSelChanged(true);
1506   }
1507
1508   public void hideColumns(int start, int end)
1509   {
1510     if (start == end)
1511     {
1512       colSel.hideSelectedColumns(start, alignment.getHiddenColumns());
1513     }
1514     else
1515     {
1516       alignment.getHiddenColumns().hideColumns(start, end);
1517     }
1518     isColSelChanged(true);
1519   }
1520
1521   public void showColumn(int col)
1522   {
1523     alignment.getHiddenColumns().revealHiddenColumns(col, colSel);
1524     isColSelChanged(true);
1525   }
1526
1527   public void showAllHiddenColumns()
1528   {
1529     alignment.getHiddenColumns().revealAllHiddenColumns(colSel);
1530     isColSelChanged(true);
1531   }
1532
1533   // common hide/show seq stuff
1534   public void showAllHiddenSeqs()
1535   {
1536     int startSeq = ranges.getStartSeq();
1537     int endSeq = ranges.getEndSeq();
1538
1539     if (alignment.getHiddenSequences().getSize() > 0)
1540     {
1541       if (selectionGroup == null)
1542       {
1543         selectionGroup = new SequenceGroup();
1544         selectionGroup.setEndRes(alignment.getWidth() - 1);
1545       }
1546       List<SequenceI> tmp = alignment.getHiddenSequences()
1547               .showAll(hiddenRepSequences);
1548       for (SequenceI seq : tmp)
1549       {
1550         selectionGroup.addSequence(seq, false);
1551         setSequenceAnnotationsVisible(seq, true);
1552       }
1553
1554       hiddenRepSequences = null;
1555
1556       ranges.setStartEndSeq(startSeq, endSeq + tmp.size());
1557
1558       firePropertyChange("alignment", null, alignment.getSequences());
1559       // used to set hasHiddenRows/hiddenRepSequences here, after the property
1560       // changed event
1561       sendSelection();
1562     }
1563   }
1564
1565   public void showSequence(int index)
1566   {
1567     int startSeq = ranges.getStartSeq();
1568     int endSeq = ranges.getEndSeq();
1569
1570     List<SequenceI> tmp = alignment.getHiddenSequences().showSequence(index,
1571             hiddenRepSequences);
1572     if (tmp.size() > 0)
1573     {
1574       if (selectionGroup == null)
1575       {
1576         selectionGroup = new SequenceGroup();
1577         selectionGroup.setEndRes(alignment.getWidth() - 1);
1578       }
1579
1580       for (SequenceI seq : tmp)
1581       {
1582         selectionGroup.addSequence(seq, false);
1583         setSequenceAnnotationsVisible(seq, true);
1584       }
1585
1586       ranges.setStartEndSeq(startSeq, endSeq + tmp.size());
1587
1588       firePropertyChange("alignment", null, alignment.getSequences());
1589       sendSelection();
1590     }
1591   }
1592
1593   public void hideAllSelectedSeqs()
1594   {
1595     if (selectionGroup == null || selectionGroup.getSize() < 1)
1596     {
1597       return;
1598     }
1599
1600     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
1601
1602     hideSequence(seqs);
1603
1604     setSelectionGroup(null);
1605   }
1606
1607   public void hideSequence(SequenceI[] seq)
1608   {
1609     /*
1610      * cache offset to first visible sequence
1611      */
1612     int startSeq = ranges.getStartSeq();
1613
1614     if (seq != null)
1615     {
1616       for (int i = 0; i < seq.length; i++)
1617       {
1618         alignment.getHiddenSequences().hideSequence(seq[i]);
1619         setSequenceAnnotationsVisible(seq[i], false);
1620       }
1621       ranges.setStartSeq(startSeq);
1622       firePropertyChange("alignment", null, alignment.getSequences());
1623     }
1624   }
1625
1626   /**
1627    * Hides the specified sequence, or the sequences it represents
1628    * 
1629    * @param sequence
1630    *          the sequence to hide, or keep as representative
1631    * @param representGroup
1632    *          if true, hide the current selection group except for the
1633    *          representative sequence
1634    */
1635   public void hideSequences(SequenceI sequence, boolean representGroup)
1636   {
1637     if (selectionGroup == null || selectionGroup.getSize() < 1)
1638     {
1639       hideSequence(new SequenceI[] { sequence });
1640       return;
1641     }
1642
1643     if (representGroup)
1644     {
1645       hideRepSequences(sequence, selectionGroup);
1646       setSelectionGroup(null);
1647       return;
1648     }
1649
1650     int gsize = selectionGroup.getSize();
1651     SequenceI[] hseqs = selectionGroup.getSequences()
1652             .toArray(new SequenceI[gsize]);
1653
1654     hideSequence(hseqs);
1655     setSelectionGroup(null);
1656     sendSelection();
1657   }
1658
1659   /**
1660    * Set visibility for any annotations for the given sequence.
1661    * 
1662    * @param sequenceI
1663    */
1664   protected void setSequenceAnnotationsVisible(SequenceI sequenceI,
1665           boolean visible)
1666   {
1667     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
1668     if (anns != null)
1669     {
1670       for (AlignmentAnnotation ann : anns)
1671       {
1672         if (ann.sequenceRef == sequenceI)
1673         {
1674           ann.visible = visible;
1675         }
1676       }
1677     }
1678   }
1679
1680   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
1681   {
1682     int sSize = sg.getSize();
1683     if (sSize < 2)
1684     {
1685       return;
1686     }
1687
1688     if (hiddenRepSequences == null)
1689     {
1690       hiddenRepSequences = new Hashtable<>();
1691     }
1692
1693     hiddenRepSequences.put(repSequence, sg);
1694
1695     // Hide all sequences except the repSequence
1696     SequenceI[] seqs = new SequenceI[sSize - 1];
1697     int index = 0;
1698     for (int i = 0; i < sSize; i++)
1699     {
1700       if (sg.getSequenceAt(i) != repSequence)
1701       {
1702         if (index == sSize - 1)
1703         {
1704           return;
1705         }
1706
1707         seqs[index++] = sg.getSequenceAt(i);
1708       }
1709     }
1710     sg.setSeqrep(repSequence); // note: not done in 2.7applet
1711     sg.setHidereps(true); // note: not done in 2.7applet
1712     hideSequence(seqs);
1713
1714   }
1715
1716   /**
1717    * 
1718    * @return null or the current reference sequence
1719    */
1720   public SequenceI getReferenceSeq()
1721   {
1722     return alignment.getSeqrep();
1723   }
1724
1725   /**
1726    * @param seq
1727    * @return true iff seq is the reference for the alignment
1728    */
1729   public boolean isReferenceSeq(SequenceI seq)
1730   {
1731     return alignment.getSeqrep() == seq;
1732   }
1733
1734   /**
1735    * 
1736    * @param seq
1737    * @return true if there are sequences represented by this sequence that are
1738    *         currently hidden
1739    */
1740   public boolean isHiddenRepSequence(SequenceI seq)
1741   {
1742     return (hiddenRepSequences != null
1743             && hiddenRepSequences.containsKey(seq));
1744   }
1745
1746   /**
1747    * 
1748    * @param seq
1749    * @return null or a sequence group containing the sequences that seq
1750    *         represents
1751    */
1752   public SequenceGroup getRepresentedSequences(SequenceI seq)
1753   {
1754     return (SequenceGroup) (hiddenRepSequences == null ? null
1755             : hiddenRepSequences.get(seq));
1756   }
1757
1758   @Override
1759   public int adjustForHiddenSeqs(int alignmentIndex)
1760   {
1761     return alignment.getHiddenSequences()
1762             .adjustForHiddenSeqs(alignmentIndex);
1763   }
1764
1765   @Override
1766   public void invertColumnSelection()
1767   {
1768     colSel.invertColumnSelection(0, alignment.getWidth(), alignment);
1769     isColSelChanged(true);
1770   }
1771
1772   @Override
1773   public SequenceI[] getSelectionAsNewSequence()
1774   {
1775     SequenceI[] sequences;
1776     // JBPNote: Need to test jalviewLite.getSelectedSequencesAsAlignmentFrom -
1777     // this was the only caller in the applet for this method
1778     // JBPNote: in applet, this method returned references to the alignment
1779     // sequences, and it did not honour the presence/absence of annotation
1780     // attached to the alignment (probably!)
1781     if (selectionGroup == null || selectionGroup.getSize() == 0)
1782     {
1783       sequences = alignment.getSequencesArray();
1784       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
1785       for (int i = 0; i < sequences.length; i++)
1786       {
1787         // construct new sequence with subset of visible annotation
1788         sequences[i] = new Sequence(sequences[i], annots);
1789       }
1790     }
1791     else
1792     {
1793       sequences = selectionGroup.getSelectionAsNewSequences(alignment);
1794     }
1795
1796     return sequences;
1797   }
1798
1799   @Override
1800   public SequenceI[] getSequenceSelection()
1801   {
1802     SequenceI[] sequences = null;
1803     if (selectionGroup != null)
1804     {
1805       sequences = selectionGroup.getSequencesInOrder(alignment);
1806     }
1807     if (sequences == null)
1808     {
1809       sequences = alignment.getSequencesArray();
1810     }
1811     return sequences;
1812   }
1813
1814   @Override
1815   public jalview.datamodel.AlignmentView getAlignmentView(
1816           boolean selectedOnly)
1817   {
1818     return getAlignmentView(selectedOnly, false);
1819   }
1820
1821   @Override
1822   public jalview.datamodel.AlignmentView getAlignmentView(
1823           boolean selectedOnly, boolean markGroups)
1824   {
1825     return new AlignmentView(alignment, alignment.getHiddenColumns(),
1826             selectionGroup,
1827             alignment.getHiddenColumns() != null
1828                     && alignment.getHiddenColumns().hasHiddenColumns(),
1829             selectedOnly, markGroups);
1830   }
1831
1832   @Override
1833   public String[] getViewAsString(boolean selectedRegionOnly)
1834   {
1835     return getViewAsString(selectedRegionOnly, true);
1836   }
1837
1838   @Override
1839   public String[] getViewAsString(boolean selectedRegionOnly,
1840           boolean exportHiddenSeqs)
1841   {
1842     String[] selection = null;
1843     SequenceI[] seqs = null;
1844     int i, iSize;
1845     int start = 0, end = 0;
1846     if (selectedRegionOnly && selectionGroup != null)
1847     {
1848       iSize = selectionGroup.getSize();
1849       seqs = selectionGroup.getSequencesInOrder(alignment);
1850       start = selectionGroup.getStartRes();
1851       end = selectionGroup.getEndRes() + 1;
1852     }
1853     else
1854     {
1855       if (hasHiddenRows() && exportHiddenSeqs)
1856       {
1857         AlignmentI fullAlignment = alignment.getHiddenSequences()
1858                 .getFullAlignment();
1859         iSize = fullAlignment.getHeight();
1860         seqs = fullAlignment.getSequencesArray();
1861         end = fullAlignment.getWidth();
1862       }
1863       else
1864       {
1865         iSize = alignment.getHeight();
1866         seqs = alignment.getSequencesArray();
1867         end = alignment.getWidth();
1868       }
1869     }
1870
1871     selection = new String[iSize];
1872     if (alignment.getHiddenColumns() != null
1873             && alignment.getHiddenColumns().hasHiddenColumns())
1874     {
1875       for (i = 0; i < iSize; i++)
1876       {
1877         Iterator<int[]> blocks = alignment.getHiddenColumns()
1878                 .getVisContigsIterator(start, end + 1, false);
1879         selection[i] = seqs[i].getSequenceStringFromIterator(blocks);
1880       }
1881     }
1882     else
1883     {
1884       for (i = 0; i < iSize; i++)
1885       {
1886         selection[i] = seqs[i].getSequenceAsString(start, end);
1887       }
1888
1889     }
1890     return selection;
1891   }
1892
1893   @Override
1894   public List<int[]> getVisibleRegionBoundaries(int min, int max)
1895   {
1896     ArrayList<int[]> regions = new ArrayList<>();
1897     int start = min;
1898     int end = max;
1899
1900     do
1901     {
1902       HiddenColumns hidden = alignment.getHiddenColumns();
1903       if (hidden != null && hidden.hasHiddenColumns())
1904       {
1905         if (start == 0)
1906         {
1907           start = hidden.visibleToAbsoluteColumn(start);
1908         }
1909
1910         end = hidden.getNextHiddenBoundary(false, start);
1911         if (start == end)
1912         {
1913           end = max;
1914         }
1915         if (end > max)
1916         {
1917           end = max;
1918         }
1919       }
1920
1921       regions.add(new int[] { start, end });
1922
1923       if (hidden != null && hidden.hasHiddenColumns())
1924       {
1925         start = hidden.visibleToAbsoluteColumn(end);
1926         start = hidden.getNextHiddenBoundary(true, start) + 1;
1927       }
1928     } while (end < max);
1929
1930     // int[][] startEnd = new int[regions.size()][2];
1931
1932     return regions;
1933   }
1934
1935   @Override
1936   public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(
1937           boolean selectedOnly)
1938   {
1939     ArrayList<AlignmentAnnotation> ala = new ArrayList<>();
1940     AlignmentAnnotation[] aa;
1941     if ((aa = alignment.getAlignmentAnnotation()) != null)
1942     {
1943       for (AlignmentAnnotation annot : aa)
1944       {
1945         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
1946         if (selectedOnly && selectionGroup != null)
1947         {
1948           clone.makeVisibleAnnotation(
1949                   selectionGroup.getStartRes(), selectionGroup.getEndRes(),
1950                   alignment.getHiddenColumns());
1951         }
1952         else
1953         {
1954           clone.makeVisibleAnnotation(alignment.getHiddenColumns());
1955         }
1956         ala.add(clone);
1957       }
1958     }
1959     return ala;
1960   }
1961
1962   @Override
1963   public boolean isPadGaps()
1964   {
1965     return padGaps;
1966   }
1967
1968   @Override
1969   public void setPadGaps(boolean padGaps)
1970   {
1971     this.padGaps = padGaps;
1972   }
1973
1974   /**
1975    * apply any post-edit constraints and trigger any calculations needed after
1976    * an edit has been performed on the alignment
1977    * 
1978    * @param ap
1979    */
1980   @Override
1981   public void alignmentChanged(AlignmentViewPanel ap)
1982   {
1983     if (isPadGaps())
1984     {
1985       alignment.padGaps();
1986     }
1987     if (autoCalculateConsensus)
1988     {
1989       updateConsensus(ap);
1990     }
1991     if (hconsensus != null && autoCalculateConsensus)
1992     {
1993       updateConservation(ap);
1994     }
1995     if (autoCalculateStrucConsensus)
1996     {
1997       updateStrucConsensus(ap);
1998     }
1999
2000     // Reset endRes of groups if beyond alignment width
2001     int alWidth = alignment.getWidth();
2002     List<SequenceGroup> groups = alignment.getGroups();
2003     if (groups != null)
2004     {
2005       for (SequenceGroup sg : groups)
2006       {
2007         if (sg.getEndRes() > alWidth)
2008         {
2009           sg.setEndRes(alWidth - 1);
2010         }
2011       }
2012     }
2013
2014     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
2015     {
2016       selectionGroup.setEndRes(alWidth - 1);
2017     }
2018
2019     updateAllColourSchemes();
2020     calculator.restartWorkers();
2021   }
2022
2023   /**
2024    * reset scope and do calculations for all applied colourschemes on alignment
2025    */
2026   void updateAllColourSchemes()
2027   {
2028     ResidueShaderI rs = residueShading;
2029     if (rs != null)
2030     {
2031       rs.alignmentChanged(alignment, hiddenRepSequences);
2032
2033       rs.setConsensus(hconsensus);
2034       if (rs.conservationApplied())
2035       {
2036         rs.setConservation(Conservation.calculateConservation("All",
2037                 alignment.getSequences(), 0, alignment.getWidth(), false,
2038                 getConsPercGaps(), false));
2039       }
2040     }
2041
2042     for (SequenceGroup sg : alignment.getGroups())
2043     {
2044       if (sg.cs != null)
2045       {
2046         sg.cs.alignmentChanged(sg, hiddenRepSequences);
2047       }
2048       sg.recalcConservation();
2049     }
2050   }
2051
2052   protected void initAutoAnnotation()
2053   {
2054     // TODO: add menu option action that nulls or creates consensus object
2055     // depending on if the user wants to see the annotation or not in a
2056     // specific alignment
2057
2058     if (hconsensus == null && !isDataset)
2059     {
2060       if (!alignment.isNucleotide())
2061       {
2062         initConservation();
2063         initQuality();
2064       }
2065       else
2066       {
2067         initRNAStructure();
2068       }
2069       consensus = new AlignmentAnnotation("Consensus",
2070               MessageManager.getString("label.consensus_descr"),
2071               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
2072       initConsensus(consensus);
2073
2074       initGapCounts();
2075
2076       initComplementConsensus();
2077     }
2078   }
2079
2080   /**
2081    * If this is a protein alignment and there are mappings to cDNA, adds the
2082    * cDNA consensus annotation and returns true, else returns false.
2083    */
2084   public boolean initComplementConsensus()
2085   {
2086     if (!alignment.isNucleotide())
2087     {
2088       final List<AlignedCodonFrame> codonMappings = alignment
2089               .getCodonFrames();
2090       if (codonMappings != null && !codonMappings.isEmpty())
2091       {
2092         boolean doConsensus = false;
2093         for (AlignedCodonFrame mapping : codonMappings)
2094         {
2095           // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame?
2096           MapList[] mapLists = mapping.getdnaToProt();
2097           // mapLists can be empty if project load has not finished resolving
2098           // seqs
2099           if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
2100           {
2101             doConsensus = true;
2102             break;
2103           }
2104         }
2105         if (doConsensus)
2106         {
2107           complementConsensus = new AlignmentAnnotation("cDNA Consensus",
2108                   MessageManager
2109                           .getString("label.complement_consensus_descr"),
2110                   new Annotation[1], 0f, 100f,
2111                   AlignmentAnnotation.BAR_GRAPH);
2112           initConsensus(complementConsensus);
2113           return true;
2114         }
2115       }
2116     }
2117     return false;
2118   }
2119
2120   private void initConsensus(AlignmentAnnotation aa)
2121   {
2122     aa.hasText = true;
2123     aa.autoCalculated = true;
2124
2125     if (showConsensus)
2126     {
2127       alignment.addAnnotation(aa);
2128     }
2129   }
2130
2131   // these should be extracted from the view model - style and settings for
2132   // derived annotation
2133   private void initGapCounts()
2134   {
2135     if (showOccupancy)
2136     {
2137       gapcounts = new AlignmentAnnotation("Occupancy",
2138               MessageManager.getString("label.occupancy_descr"),
2139               new Annotation[1], 0f, alignment.getHeight(),
2140               AlignmentAnnotation.BAR_GRAPH);
2141       gapcounts.hasText = true;
2142       gapcounts.autoCalculated = true;
2143       gapcounts.scaleColLabel = true;
2144       gapcounts.graph = AlignmentAnnotation.BAR_GRAPH;
2145
2146       alignment.addAnnotation(gapcounts);
2147     }
2148   }
2149
2150   private void initConservation()
2151   {
2152     if (showConservation)
2153     {
2154       if (conservation == null)
2155       {
2156         conservation = new AlignmentAnnotation("Conservation",
2157                 MessageManager.formatMessage("label.conservation_descr",
2158                         getConsPercGaps()),
2159                 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
2160         conservation.hasText = true;
2161         conservation.autoCalculated = true;
2162         alignment.addAnnotation(conservation);
2163       }
2164     }
2165   }
2166
2167   private void initQuality()
2168   {
2169     if (showQuality)
2170     {
2171       if (quality == null)
2172       {
2173         quality = new AlignmentAnnotation("Quality",
2174                 MessageManager.getString("label.quality_descr"),
2175                 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
2176         quality.hasText = true;
2177         quality.autoCalculated = true;
2178         alignment.addAnnotation(quality);
2179       }
2180     }
2181   }
2182
2183   private void initRNAStructure()
2184   {
2185     if (alignment.hasRNAStructure() && strucConsensus == null)
2186     {
2187       strucConsensus = new AlignmentAnnotation("StrucConsensus",
2188               MessageManager.getString("label.strucconsensus_descr"),
2189               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
2190       strucConsensus.hasText = true;
2191       strucConsensus.autoCalculated = true;
2192
2193       if (showConsensus)
2194       {
2195         alignment.addAnnotation(strucConsensus);
2196       }
2197     }
2198   }
2199
2200   /*
2201    * (non-Javadoc)
2202    * 
2203    * @see jalview.api.AlignViewportI#calcPanelHeight()
2204    */
2205   @Override
2206   public int calcPanelHeight()
2207   {
2208     // setHeight of panels
2209     AlignmentAnnotation[] anns = getAlignment().getAlignmentAnnotation();
2210     int height = 0;
2211     int charHeight = getCharHeight();
2212     if (anns != null)
2213     {
2214       BitSet graphgrp = new BitSet();
2215       for (AlignmentAnnotation aa : anns)
2216       {
2217         if (aa == null)
2218         {
2219           System.err.println("Null annotation row: ignoring.");
2220           continue;
2221         }
2222         if (!aa.visible)
2223         {
2224           continue;
2225         }
2226         if (aa.graphGroup > -1)
2227         {
2228           if (graphgrp.get(aa.graphGroup))
2229           {
2230             continue;
2231           }
2232           else
2233           {
2234             graphgrp.set(aa.graphGroup);
2235           }
2236         }
2237         aa.height = 0;
2238
2239         if (aa.hasText)
2240         {
2241           aa.height += charHeight;
2242         }
2243
2244         if (aa.hasIcons)
2245         {
2246           aa.height += 16;
2247         }
2248
2249         if (aa.graph > 0)
2250         {
2251           aa.height += aa.graphHeight;
2252         }
2253
2254         if (aa.height == 0)
2255         {
2256           aa.height = 20;
2257         }
2258
2259         height += aa.height;
2260       }
2261     }
2262     if (height == 0)
2263     {
2264       // set minimum
2265       height = 20;
2266     }
2267     return height;
2268   }
2269
2270   @Override
2271   public void updateGroupAnnotationSettings(boolean applyGlobalSettings,
2272           boolean preserveNewGroupSettings)
2273   {
2274     boolean updateCalcs = false;
2275     boolean conv = isShowGroupConservation();
2276     boolean cons = isShowGroupConsensus();
2277     boolean showprf = isShowSequenceLogo();
2278     boolean showConsHist = isShowConsensusHistogram();
2279     boolean normLogo = isNormaliseSequenceLogo();
2280     boolean showHMMPrf = isShowHMMSequenceLogo();
2281     boolean showInfoHist = isShowInformationHistogram();
2282     boolean normHMMLogo = isNormaliseHMMSequenceLogo();
2283
2284     /**
2285      * TODO reorder the annotation rows according to group/sequence ordering on
2286      * alignment
2287      */
2288     // boolean sortg = true;
2289
2290     // remove old automatic annotation
2291     // add any new annotation
2292
2293     // intersect alignment annotation with alignment groups
2294
2295     AlignmentAnnotation[] aan = alignment.getAlignmentAnnotation();
2296     List<SequenceGroup> oldrfs = new ArrayList<>();
2297     if (aan != null)
2298     {
2299       for (int an = 0; an < aan.length; an++)
2300       {
2301         if (aan[an].autoCalculated && aan[an].groupRef != null)
2302         {
2303           oldrfs.add(aan[an].groupRef);
2304           alignment.deleteAnnotation(aan[an], false);
2305         }
2306       }
2307     }
2308     if (alignment.getGroups() != null)
2309     {
2310       for (SequenceGroup sg : alignment.getGroups())
2311       {
2312         updateCalcs = false;
2313         if (applyGlobalSettings
2314                 || (!preserveNewGroupSettings && !oldrfs.contains(sg)))
2315         {
2316           // set defaults for this group's conservation/consensus
2317           sg.setshowSequenceLogo(showprf);
2318           sg.setShowConsensusHistogram(showConsHist);
2319           sg.setNormaliseSequenceLogo(normLogo);
2320           sg.setShowHMMSequenceLogo(showHMMPrf);
2321           sg.setShowInformationHistogram(showInfoHist);
2322           sg.setNormaliseHMMSequenceLogo(normHMMLogo);
2323         }
2324         if (conv)
2325         {
2326           updateCalcs = true;
2327           alignment.addAnnotation(sg.getConservationRow(), 0);
2328         }
2329         if (cons)
2330         {
2331           updateCalcs = true;
2332           alignment.addAnnotation(sg.getConsensus(), 0);
2333         }
2334         // refresh the annotation rows
2335         if (updateCalcs)
2336         {
2337           sg.recalcConservation();
2338         }
2339       }
2340     }
2341     oldrfs.clear();
2342   }
2343
2344   @Override
2345   public boolean isDisplayReferenceSeq()
2346   {
2347     return alignment.hasSeqrep() && viewStyle.isDisplayReferenceSeq();
2348   }
2349
2350   @Override
2351   public void setDisplayReferenceSeq(boolean displayReferenceSeq)
2352   {
2353     viewStyle.setDisplayReferenceSeq(displayReferenceSeq);
2354   }
2355
2356   @Override
2357   public boolean isColourByReferenceSeq()
2358   {
2359     return alignment.hasSeqrep() && viewStyle.isColourByReferenceSeq();
2360   }
2361
2362   @Override
2363   public Color getSequenceColour(SequenceI seq)
2364   {
2365     Color sqc = sequenceColours.get(seq);
2366     return (sqc == null ? Color.white : sqc);
2367   }
2368
2369   @Override
2370   public void setSequenceColour(SequenceI seq, Color col)
2371   {
2372     if (col == null)
2373     {
2374       sequenceColours.remove(seq);
2375     }
2376     else
2377     {
2378       sequenceColours.put(seq, col);
2379     }
2380   }
2381
2382   @Override
2383   public void updateSequenceIdColours()
2384   {
2385     for (SequenceGroup sg : alignment.getGroups())
2386     {
2387       if (sg.idColour != null)
2388       {
2389         for (SequenceI s : sg.getSequences(getHiddenRepSequences()))
2390         {
2391           sequenceColours.put(s, sg.idColour);
2392         }
2393       }
2394     }
2395   }
2396
2397   @Override
2398   public void clearSequenceColours()
2399   {
2400     sequenceColours.clear();
2401   }
2402
2403   @Override
2404   public AlignViewportI getCodingComplement()
2405   {
2406     return this.codingComplement;
2407   }
2408
2409   /**
2410    * Set this as the (cDna/protein) complement of the given viewport. Also
2411    * ensures the reverse relationship is set on the given viewport.
2412    */
2413   @Override
2414   public void setCodingComplement(AlignViewportI av)
2415   {
2416     if (this == av)
2417     {
2418       System.err.println("Ignoring recursive setCodingComplement request");
2419     }
2420     else
2421     {
2422       this.codingComplement = av;
2423       // avoid infinite recursion!
2424       if (av.getCodingComplement() != this)
2425       {
2426         av.setCodingComplement(this);
2427       }
2428     }
2429   }
2430
2431   @Override
2432   public boolean isNucleotide()
2433   {
2434     return getAlignment() == null ? false : getAlignment().isNucleotide();
2435   }
2436
2437   @Override
2438   public FeaturesDisplayedI getFeaturesDisplayed()
2439   {
2440     return featuresDisplayed;
2441   }
2442
2443   @Override
2444   public void setFeaturesDisplayed(FeaturesDisplayedI featuresDisplayedI)
2445   {
2446     featuresDisplayed = featuresDisplayedI;
2447   }
2448
2449   @Override
2450   public boolean areFeaturesDisplayed()
2451   {
2452     return featuresDisplayed != null
2453             && featuresDisplayed.getRegisteredFeaturesCount() > 0;
2454   }
2455
2456   /**
2457    * set the flag
2458    * 
2459    * @param b
2460    *          features are displayed if true
2461    */
2462   @Override
2463   public void setShowSequenceFeatures(boolean b)
2464   {
2465     viewStyle.setShowSequenceFeatures(b);
2466   }
2467
2468   @Override
2469   public boolean isShowSequenceFeatures()
2470   {
2471     return viewStyle.isShowSequenceFeatures();
2472   }
2473
2474   @Override
2475   public void setShowSequenceFeaturesHeight(boolean selected)
2476   {
2477     viewStyle.setShowSequenceFeaturesHeight(selected);
2478   }
2479
2480   @Override
2481   public boolean isShowSequenceFeaturesHeight()
2482   {
2483     return viewStyle.isShowSequenceFeaturesHeight();
2484   }
2485
2486   @Override
2487   public void setShowAnnotation(boolean b)
2488   {
2489     viewStyle.setShowAnnotation(b);
2490   }
2491
2492   @Override
2493   public boolean isShowAnnotation()
2494   {
2495     return viewStyle.isShowAnnotation();
2496   }
2497
2498   @Override
2499   public boolean isRightAlignIds()
2500   {
2501     return viewStyle.isRightAlignIds();
2502   }
2503
2504   @Override
2505   public void setRightAlignIds(boolean rightAlignIds)
2506   {
2507     viewStyle.setRightAlignIds(rightAlignIds);
2508   }
2509
2510   @Override
2511   public boolean getConservationSelected()
2512   {
2513     return viewStyle.getConservationSelected();
2514   }
2515
2516   @Override
2517   public void setShowBoxes(boolean state)
2518   {
2519     viewStyle.setShowBoxes(state);
2520   }
2521
2522   /**
2523    * @return
2524    * @see jalview.api.ViewStyleI#getTextColour()
2525    */
2526   @Override
2527   public Color getTextColour()
2528   {
2529     return viewStyle.getTextColour();
2530   }
2531
2532   /**
2533    * @return
2534    * @see jalview.api.ViewStyleI#getTextColour2()
2535    */
2536   @Override
2537   public Color getTextColour2()
2538   {
2539     return viewStyle.getTextColour2();
2540   }
2541
2542   /**
2543    * @return
2544    * @see jalview.api.ViewStyleI#getThresholdTextColour()
2545    */
2546   @Override
2547   public int getThresholdTextColour()
2548   {
2549     return viewStyle.getThresholdTextColour();
2550   }
2551
2552   /**
2553    * @return
2554    * @see jalview.api.ViewStyleI#isConservationColourSelected()
2555    */
2556   @Override
2557   public boolean isConservationColourSelected()
2558   {
2559     return viewStyle.isConservationColourSelected();
2560   }
2561
2562   /**
2563    * @return
2564    * @see jalview.api.ViewStyleI#isRenderGaps()
2565    */
2566   @Override
2567   public boolean isRenderGaps()
2568   {
2569     return viewStyle.isRenderGaps();
2570   }
2571
2572   /**
2573    * @return
2574    * @see jalview.api.ViewStyleI#isShowColourText()
2575    */
2576   @Override
2577   public boolean isShowColourText()
2578   {
2579     return viewStyle.isShowColourText();
2580   }
2581
2582   /**
2583    * @param conservationColourSelected
2584    * @see jalview.api.ViewStyleI#setConservationColourSelected(boolean)
2585    */
2586   @Override
2587   public void setConservationColourSelected(
2588           boolean conservationColourSelected)
2589   {
2590     viewStyle.setConservationColourSelected(conservationColourSelected);
2591   }
2592
2593   /**
2594    * @param showColourText
2595    * @see jalview.api.ViewStyleI#setShowColourText(boolean)
2596    */
2597   @Override
2598   public void setShowColourText(boolean showColourText)
2599   {
2600     viewStyle.setShowColourText(showColourText);
2601   }
2602
2603   /**
2604    * @param textColour
2605    * @see jalview.api.ViewStyleI#setTextColour(java.awt.Color)
2606    */
2607   @Override
2608   public void setTextColour(Color textColour)
2609   {
2610     viewStyle.setTextColour(textColour);
2611   }
2612
2613   /**
2614    * @param thresholdTextColour
2615    * @see jalview.api.ViewStyleI#setThresholdTextColour(int)
2616    */
2617   @Override
2618   public void setThresholdTextColour(int thresholdTextColour)
2619   {
2620     viewStyle.setThresholdTextColour(thresholdTextColour);
2621   }
2622
2623   /**
2624    * @param textColour2
2625    * @see jalview.api.ViewStyleI#setTextColour2(java.awt.Color)
2626    */
2627   @Override
2628   public void setTextColour2(Color textColour2)
2629   {
2630     viewStyle.setTextColour2(textColour2);
2631   }
2632
2633   @Override
2634   public ViewStyleI getViewStyle()
2635   {
2636     return new ViewStyle(viewStyle);
2637   }
2638
2639   @Override
2640   public void setViewStyle(ViewStyleI settingsForView)
2641   {
2642     viewStyle = new ViewStyle(settingsForView);
2643     if (residueShading != null)
2644     {
2645       residueShading.setConservationApplied(
2646               settingsForView.isConservationColourSelected());
2647     }
2648   }
2649
2650   @Override
2651   public boolean sameStyle(ViewStyleI them)
2652   {
2653     return viewStyle.sameStyle(them);
2654   }
2655
2656   /**
2657    * @return
2658    * @see jalview.api.ViewStyleI#getIdWidth()
2659    */
2660   @Override
2661   public int getIdWidth()
2662   {
2663     return viewStyle.getIdWidth();
2664   }
2665
2666   /**
2667    * @param i
2668    * @see jalview.api.ViewStyleI#setIdWidth(int)
2669    */
2670   @Override
2671   public void setIdWidth(int i)
2672   {
2673     viewStyle.setIdWidth(i);
2674   }
2675
2676   /**
2677    * @return
2678    * @see jalview.api.ViewStyleI#isCentreColumnLabels()
2679    */
2680   @Override
2681   public boolean isCentreColumnLabels()
2682   {
2683     return viewStyle.isCentreColumnLabels();
2684   }
2685
2686   /**
2687    * @param centreColumnLabels
2688    * @see jalview.api.ViewStyleI#setCentreColumnLabels(boolean)
2689    */
2690   @Override
2691   public void setCentreColumnLabels(boolean centreColumnLabels)
2692   {
2693     viewStyle.setCentreColumnLabels(centreColumnLabels);
2694   }
2695
2696   /**
2697    * @param showdbrefs
2698    * @see jalview.api.ViewStyleI#setShowDBRefs(boolean)
2699    */
2700   @Override
2701   public void setShowDBRefs(boolean showdbrefs)
2702   {
2703     viewStyle.setShowDBRefs(showdbrefs);
2704   }
2705
2706   /**
2707    * @return
2708    * @see jalview.api.ViewStyleI#isShowDBRefs()
2709    */
2710   @Override
2711   public boolean isShowDBRefs()
2712   {
2713     return viewStyle.isShowDBRefs();
2714   }
2715
2716   /**
2717    * @return
2718    * @see jalview.api.ViewStyleI#isShowNPFeats()
2719    */
2720   @Override
2721   public boolean isShowNPFeats()
2722   {
2723     return viewStyle.isShowNPFeats();
2724   }
2725
2726   /**
2727    * @param shownpfeats
2728    * @see jalview.api.ViewStyleI#setShowNPFeats(boolean)
2729    */
2730   @Override
2731   public void setShowNPFeats(boolean shownpfeats)
2732   {
2733     viewStyle.setShowNPFeats(shownpfeats);
2734   }
2735
2736   public abstract StructureSelectionManager getStructureSelectionManager();
2737
2738   /**
2739    * Add one command to the command history list.
2740    * 
2741    * @param command
2742    */
2743   public void addToHistoryList(CommandI command)
2744   {
2745     if (this.historyList != null)
2746     {
2747       this.historyList.push(command);
2748       broadcastCommand(command, false);
2749     }
2750   }
2751
2752   protected void broadcastCommand(CommandI command, boolean undo)
2753   {
2754     getStructureSelectionManager().commandPerformed(command, undo,
2755             getVamsasSource());
2756   }
2757
2758   /**
2759    * Add one command to the command redo list.
2760    * 
2761    * @param command
2762    */
2763   public void addToRedoList(CommandI command)
2764   {
2765     if (this.redoList != null)
2766     {
2767       this.redoList.push(command);
2768     }
2769     broadcastCommand(command, true);
2770   }
2771
2772   /**
2773    * Clear the command redo list.
2774    */
2775   public void clearRedoList()
2776   {
2777     if (this.redoList != null)
2778     {
2779       this.redoList.clear();
2780     }
2781   }
2782
2783   public void setHistoryList(Deque<CommandI> list)
2784   {
2785     this.historyList = list;
2786   }
2787
2788   public Deque<CommandI> getHistoryList()
2789   {
2790     return this.historyList;
2791   }
2792
2793   public void setRedoList(Deque<CommandI> list)
2794   {
2795     this.redoList = list;
2796   }
2797
2798   public Deque<CommandI> getRedoList()
2799   {
2800     return this.redoList;
2801   }
2802
2803   @Override
2804   public VamsasSource getVamsasSource()
2805   {
2806     return this;
2807   }
2808
2809   public SequenceAnnotationOrder getSortAnnotationsBy()
2810   {
2811     return sortAnnotationsBy;
2812   }
2813
2814   public void setSortAnnotationsBy(
2815           SequenceAnnotationOrder sortAnnotationsBy)
2816   {
2817     this.sortAnnotationsBy = sortAnnotationsBy;
2818   }
2819
2820   public boolean isShowAutocalculatedAbove()
2821   {
2822     return showAutocalculatedAbove;
2823   }
2824
2825   public void setShowAutocalculatedAbove(boolean showAutocalculatedAbove)
2826   {
2827     this.showAutocalculatedAbove = showAutocalculatedAbove;
2828   }
2829
2830   @Override
2831   public boolean isScaleProteinAsCdna()
2832   {
2833     return viewStyle.isScaleProteinAsCdna();
2834   }
2835
2836   @Override
2837   public void setScaleProteinAsCdna(boolean b)
2838   {
2839     viewStyle.setScaleProteinAsCdna(b);
2840   }
2841
2842   @Override
2843   public boolean isProteinFontAsCdna()
2844   {
2845     return viewStyle.isProteinFontAsCdna();
2846   }
2847
2848   @Override
2849   public void setProteinFontAsCdna(boolean b)
2850   {
2851     viewStyle.setProteinFontAsCdna(b);
2852   }
2853
2854   @Override
2855   public void setShowComplementFeatures(boolean b)
2856   {
2857     viewStyle.setShowComplementFeatures(b);
2858   }
2859
2860   @Override
2861   public boolean isShowComplementFeatures()
2862   {
2863     return viewStyle.isShowComplementFeatures();
2864   }
2865
2866   @Override
2867   public void setShowComplementFeaturesOnTop(boolean b)
2868   {
2869     viewStyle.setShowComplementFeaturesOnTop(b);
2870   }
2871
2872   @Override
2873   public boolean isShowComplementFeaturesOnTop()
2874   {
2875     return viewStyle.isShowComplementFeaturesOnTop();
2876   }
2877
2878   /**
2879    * @return true if view should scroll to show the highlighted region of a
2880    *         sequence
2881    * @return
2882    */
2883   @Override
2884   public final boolean isFollowHighlight()
2885   {
2886     return followHighlight;
2887   }
2888
2889   @Override
2890   public final void setFollowHighlight(boolean b)
2891   {
2892     this.followHighlight = b;
2893   }
2894
2895   @Override
2896   public ViewportRanges getRanges()
2897   {
2898     return ranges;
2899   }
2900
2901   /**
2902    * Helper method to populate the SearchResults with the location in the
2903    * complementary alignment to scroll to, in order to match this one.
2904    * 
2905    * @param sr
2906    *          the SearchResults to add to
2907    * @return the offset (below top of visible region) of the matched sequence
2908    */
2909   protected int findComplementScrollTarget(SearchResultsI sr)
2910   {
2911     final AlignViewportI complement = getCodingComplement();
2912     if (complement == null || !complement.isFollowHighlight())
2913     {
2914       return 0;
2915     }
2916     boolean iAmProtein = !getAlignment().isNucleotide();
2917     AlignmentI proteinAlignment = iAmProtein ? getAlignment()
2918             : complement.getAlignment();
2919     if (proteinAlignment == null)
2920     {
2921       return 0;
2922     }
2923     final List<AlignedCodonFrame> mappings = proteinAlignment
2924             .getCodonFrames();
2925
2926     /*
2927      * Heuristic: find the first mapped sequence (if any) with a non-gapped
2928      * residue in the middle column of the visible region. Scroll the
2929      * complementary alignment to line up the corresponding residue.
2930      */
2931     int seqOffset = 0;
2932     SequenceI sequence = null;
2933
2934     /*
2935      * locate 'middle' column (true middle if an odd number visible, left of
2936      * middle if an even number visible)
2937      */
2938     int middleColumn = ranges.getStartRes()
2939             + (ranges.getEndRes() - ranges.getStartRes()) / 2;
2940     final HiddenSequences hiddenSequences = getAlignment()
2941             .getHiddenSequences();
2942
2943     /*
2944      * searching to the bottom of the alignment gives smoother scrolling across
2945      * all gapped visible regions
2946      */
2947     int lastSeq = alignment.getHeight() - 1;
2948     List<AlignedCodonFrame> seqMappings = null;
2949     for (int seqNo = ranges
2950             .getStartSeq(); seqNo <= lastSeq; seqNo++, seqOffset++)
2951     {
2952       sequence = getAlignment().getSequenceAt(seqNo);
2953       if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
2954       {
2955         continue;
2956       }
2957       if (Comparison.isGap(sequence.getCharAt(middleColumn)))
2958       {
2959         continue;
2960       }
2961       seqMappings = MappingUtils.findMappingsForSequenceAndOthers(sequence,
2962               mappings,
2963               getCodingComplement().getAlignment().getSequences());
2964       if (!seqMappings.isEmpty())
2965       {
2966         break;
2967       }
2968     }
2969
2970     if (sequence == null || seqMappings == null || seqMappings.isEmpty())
2971     {
2972       /*
2973        * No ungapped mapped sequence in middle column - do nothing
2974        */
2975       return 0;
2976     }
2977     MappingUtils.addSearchResults(sr, sequence,
2978             sequence.findPosition(middleColumn), seqMappings);
2979     return seqOffset;
2980   }
2981
2982   /**
2983    * synthesize a column selection if none exists so it covers the given
2984    * selection group. if wholewidth is false, no column selection is made if the
2985    * selection group covers the whole alignment width.
2986    * 
2987    * @param sg
2988    * @param wholewidth
2989    */
2990   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
2991   {
2992     int sgs, sge;
2993     if (sg != null && (sgs = sg.getStartRes()) >= 0
2994             && sg.getStartRes() <= (sge = sg.getEndRes())
2995             && !this.hasSelectedColumns())
2996     {
2997       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
2998       {
2999         // do nothing
3000         return;
3001       }
3002       if (colSel == null)
3003       {
3004         colSel = new ColumnSelection();
3005       }
3006       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
3007       {
3008         colSel.addElement(cspos);
3009       }
3010     }
3011   }
3012
3013   /**
3014    * hold status of current selection group - defined on alignment or not.
3015    */
3016   private boolean selectionIsDefinedGroup = false;
3017
3018   @Override
3019   public boolean isSelectionDefinedGroup()
3020   {
3021     if (selectionGroup == null)
3022     {
3023       return false;
3024     }
3025     if (isSelectionGroupChanged(true))
3026     {
3027       selectionIsDefinedGroup = false;
3028       List<SequenceGroup> gps = alignment.getGroups();
3029       if (gps == null || gps.size() == 0)
3030       {
3031         selectionIsDefinedGroup = false;
3032       }
3033       else
3034       {
3035         selectionIsDefinedGroup = gps.contains(selectionGroup);
3036       }
3037     }
3038     return selectionGroup.isDefined() || selectionIsDefinedGroup;
3039   }
3040
3041   /**
3042    * null, or currently highlighted results on this view
3043    */
3044   private SearchResultsI searchResults = null;
3045
3046   protected TreeModel currentTree = null;
3047
3048   @Override
3049   public boolean hasSearchResults()
3050   {
3051     return searchResults != null;
3052   }
3053
3054   @Override
3055   public void setSearchResults(SearchResultsI results)
3056   {
3057     searchResults = results;
3058   }
3059
3060   @Override
3061   public SearchResultsI getSearchResults()
3062   {
3063     return searchResults;
3064   }
3065
3066   /**
3067    * get the consensus sequence as displayed under the PID consensus annotation
3068    * row.
3069    * 
3070    * @return consensus sequence as a new sequence object
3071    */
3072   public SequenceI getConsensusSeq()
3073   {
3074     if (consensus == null)
3075     {
3076       updateConsensus(null);
3077     }
3078     if (consensus == null)
3079     {
3080       return null;
3081     }
3082     StringBuffer seqs = new StringBuffer();
3083     for (int i = 0; i < consensus.annotations.length; i++)
3084     {
3085       Annotation annotation = consensus.annotations[i];
3086       if (annotation != null)
3087       {
3088         String description = annotation.description;
3089         if (description != null && description.startsWith("["))
3090         {
3091           // consensus is a tie - just pick the first one
3092           seqs.append(description.charAt(1));
3093         }
3094         else
3095         {
3096           seqs.append(annotation.displayCharacter);
3097         }
3098       }
3099     }
3100
3101     SequenceI sq = new Sequence("Consensus", seqs.toString());
3102     sq.setDescription("Percentage Identity Consensus "
3103             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
3104     return sq;
3105   }
3106
3107   public boolean hasReferenceAnnotation()
3108   {
3109     AlignmentAnnotation[] annots = this.alignment.getAlignmentAnnotation();
3110     for (AlignmentAnnotation annot : annots)
3111     {
3112       if ("RF".equals(annot.label) || annot.label.contains("Reference"))
3113       {
3114         return true;
3115       }
3116     }
3117     return false;
3118   }
3119
3120   @Override
3121   public void setCurrentTree(TreeModel tree)
3122   {
3123     currentTree = tree;
3124   }
3125
3126   @Override
3127   public TreeModel getCurrentTree()
3128   {
3129     return currentTree;
3130   }
3131
3132   @Override
3133   public AlignmentExportData getAlignExportData(AlignExportSettingsI options)
3134   {
3135     AlignmentI alignmentToExport = null;
3136     String[] omitHidden = null;
3137     alignmentToExport = null;
3138
3139     if (hasHiddenColumns() && !options.isExportHiddenColumns())
3140     {
3141       omitHidden = getViewAsString(false,
3142               options.isExportHiddenSequences());
3143     }
3144
3145     int[] alignmentStartEnd = new int[2];
3146     if (hasHiddenRows() && options.isExportHiddenSequences())
3147     {
3148       alignmentToExport = getAlignment().getHiddenSequences()
3149               .getFullAlignment();
3150     }
3151     else
3152     {
3153       alignmentToExport = getAlignment();
3154     }
3155     alignmentStartEnd = getAlignment().getHiddenColumns()
3156             .getVisibleStartAndEndIndex(alignmentToExport.getWidth());
3157     AlignmentExportData ed = new AlignmentExportData(alignmentToExport,
3158             omitHidden, alignmentStartEnd);
3159     return ed;
3160   }
3161   
3162   @Override
3163   public boolean isNormaliseSequenceLogo()
3164   {
3165     return normaliseSequenceLogo;
3166   }
3167
3168   public void setNormaliseSequenceLogo(boolean state)
3169   {
3170     normaliseSequenceLogo = state;
3171   }
3172
3173   @Override
3174   public boolean isNormaliseHMMSequenceLogo()
3175   {
3176     return hmmNormaliseSequenceLogo;
3177   }
3178
3179   public void setNormaliseHMMSequenceLogo(boolean state)
3180   {
3181     hmmNormaliseSequenceLogo = state;
3182   }
3183   /**
3184    * flag set to indicate if structure views might be out of sync with sequences
3185    * in the alignment
3186    */
3187
3188   private boolean needToUpdateStructureViews = false;
3189
3190   @Override
3191   public boolean isUpdateStructures()
3192   {
3193     return needToUpdateStructureViews;
3194   }
3195
3196   @Override
3197   public void setUpdateStructures(boolean update)
3198   {
3199     needToUpdateStructureViews = update;
3200   }
3201
3202   @Override
3203   public boolean needToUpdateStructureViews()
3204   {
3205     boolean update = needToUpdateStructureViews;
3206     needToUpdateStructureViews = false;
3207     return update;
3208   }
3209
3210   @Override
3211   public void addSequenceGroup(SequenceGroup sequenceGroup)
3212   {
3213     alignment.addGroup(sequenceGroup);
3214
3215     Color col = sequenceGroup.idColour;
3216     if (col != null)
3217     {
3218       col = col.brighter();
3219
3220       for (SequenceI sq : sequenceGroup.getSequences())
3221       {
3222         setSequenceColour(sq, col);
3223       }
3224     }
3225
3226     if (codingComplement != null)
3227     {
3228       SequenceGroup mappedGroup = MappingUtils
3229               .mapSequenceGroup(sequenceGroup, this, codingComplement);
3230       if (mappedGroup.getSequences().size() > 0)
3231       {
3232         codingComplement.getAlignment().addGroup(mappedGroup);
3233
3234         if (col != null)
3235         {
3236           for (SequenceI seq : mappedGroup.getSequences())
3237           {
3238             codingComplement.setSequenceColour(seq, col);
3239           }
3240         }
3241       }
3242       // propagate the structure view update flag according to our own setting
3243       codingComplement.setUpdateStructures(needToUpdateStructureViews);
3244     }
3245   }
3246
3247   /**
3248    * Filters out sequences with an eValue higher than the specified value. The
3249    * filtered sequences are hidden or deleted. Sequences with no eValues are also
3250    * filtered out.
3251    * 
3252    * @param eValue
3253    * @param delete
3254    */
3255   public void filterByEvalue(double eValue)
3256   {
3257     for (SequenceI seq : alignment.getSequencesArray())
3258     {
3259       if ((seq.getAnnotation("Search Scores") == null
3260               || seq.getAnnotation("Search Scores")[0].getEValue() > eValue)
3261               && seq.getHMM() == null)
3262       {
3263         hideSequence(new SequenceI[] { seq });
3264       }
3265     }
3266   }
3267
3268   /**
3269    * Filters out sequences with an score lower than the specified value. The
3270    * filtered sequences are hidden or deleted.
3271    * 
3272    * @param score
3273    * @param delete
3274    */
3275   public void filterByScore(double score)
3276   {
3277     for (SequenceI seq : alignment.getSequencesArray())
3278     {
3279       if ((seq.getAnnotation("Search Scores") == null
3280               || seq.getAnnotation("Search Scores")[0]
3281                       .getBitScore() < score)
3282               && seq.getHMM() == null)
3283       {
3284         hideSequence(new SequenceI[] { seq });
3285       }
3286     }
3287   }
3288 }