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