JAL-2629 add documentation to previous work
[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   /**
714    * results of information annotation analysis for the visible portion of view
715    */
716   protected List<ProfilesI> hinformation = new ArrayList<>();
717
718   /**
719    * results of cDNA complement consensus visible portion of view
720    */
721   protected Hashtable[] hcomplementConsensus = null;
722
723   /**
724    * results of secondary structure base pair consensus for visible portion of
725    * view
726    */
727   protected Hashtable[] hStrucConsensus = null;
728
729   protected Conservation hconservation = null;
730
731   @Override
732   public void setConservation(Conservation cons)
733   {
734     hconservation = cons;
735   }
736
737   /**
738    * percentage gaps allowed in a column before all amino acid properties should
739    * be considered unconserved
740    */
741   int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
742
743   @Override
744   public int getConsPercGaps()
745   {
746     return ConsPercGaps;
747   }
748
749   @Override
750   public void setSequenceConsensusHash(ProfilesI hconsensus)
751   {
752     this.hconsensus = hconsensus;
753   }
754
755   @Override
756   public void setComplementConsensusHash(Hashtable[] hconsensus)
757   {
758     this.hcomplementConsensus = hconsensus;
759   }
760
761   @Override
762   public ProfilesI getSequenceConsensusHash()
763   {
764     return hconsensus;
765   }
766
767   @Override
768   public void setSequenceInformationHashes(List<ProfilesI> info)
769   {
770     hinformation = info;
771   }
772
773   @Override
774   public void setSequenceInformationHash(ProfilesI info, int index)
775   {
776     hinformation.set(index, info);
777   }
778
779   @Override
780   public List<ProfilesI> getSequenceInformationHashes()
781   {
782     return hinformation;
783   }
784
785   @Override
786   public ProfilesI getSequenceInformationHash(int index)
787   {
788     return hinformation.get(index);
789   }
790
791   @Override
792   public Hashtable[] getComplementConsensusHash()
793   {
794     return hcomplementConsensus;
795   }
796
797   @Override
798   public Hashtable[] getRnaStructureConsensusHash()
799   {
800     return hStrucConsensus;
801   }
802
803   @Override
804   public void setRnaStructureConsensusHash(Hashtable[] hStrucConsensus)
805   {
806     this.hStrucConsensus = hStrucConsensus;
807
808   }
809
810   @Override
811   public AlignmentAnnotation getAlignmentQualityAnnot()
812   {
813     return quality;
814   }
815
816   @Override
817   public AlignmentAnnotation getAlignmentConservationAnnotation()
818   {
819     return conservation;
820   }
821
822   @Override
823   public AlignmentAnnotation getAlignmentConsensusAnnotation()
824   {
825     return consensus;
826   }
827
828   @Override
829   public List<AlignmentAnnotation> getInformationAnnotations()
830   {
831     return information;
832   }
833
834   @Override
835   public AlignmentAnnotation getInformationAnnotation(int index)
836   {
837     return information.get(index);
838   }
839
840   @Override
841   public AlignmentAnnotation getAlignmentGapAnnotation()
842   {
843     return gapcounts;
844   }
845
846   @Override
847   public AlignmentAnnotation getComplementConsensusAnnotation()
848   {
849     return complementConsensus;
850   }
851
852   @Override
853   public AlignmentAnnotation getAlignmentStrucConsensusAnnotation()
854   {
855     return strucConsensus;
856   }
857
858   protected AlignCalcManagerI calculator = new AlignCalcManager();
859
860   /**
861    * trigger update of conservation annotation
862    */
863   public void updateConservation(final AlignmentViewPanel ap)
864   {
865     // see note in mantis : issue number 8585
866     if (alignment.isNucleotide()
867             || (conservation == null && quality == null)
868             || !autoCalculateConsensus)
869     {
870       return;
871     }
872     if (calculator.getRegisteredWorkersOfClass(
873             jalview.workers.ConservationThread.class) == null)
874     {
875       calculator.registerWorker(
876               new jalview.workers.ConservationThread(this, ap));
877     }
878   }
879
880   /**
881    * trigger update of consensus annotation
882    */
883   public void updateConsensus(final AlignmentViewPanel ap)
884   {
885     // see note in mantis : issue number 8585
886     if (consensus == null || !autoCalculateConsensus)
887     {
888       return;
889     }
890     if (calculator
891             .getRegisteredWorkersOfClass(ConsensusThread.class) == null)
892     {
893       calculator.registerWorker(new ConsensusThread(this, ap));
894     }
895
896     /*
897      * A separate thread to compute cDNA consensus for a protein alignment
898      * which has mapping to cDNA
899      */
900     final AlignmentI al = this.getAlignment();
901     if (!al.isNucleotide() && al.getCodonFrames() != null
902             && !al.getCodonFrames().isEmpty())
903     {
904       /*
905        * fudge - check first for protein-to-nucleotide mappings
906        * (we don't want to do this for protein-to-protein)
907        */
908       boolean doConsensus = false;
909       for (AlignedCodonFrame mapping : al.getCodonFrames())
910       {
911         // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame?
912         MapList[] mapLists = mapping.getdnaToProt();
913         // mapLists can be empty if project load has not finished resolving seqs
914         if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
915         {
916           doConsensus = true;
917           break;
918         }
919       }
920       if (doConsensus)
921       {
922         if (calculator.getRegisteredWorkersOfClass(
923                 ComplementConsensusThread.class) == null)
924         {
925           calculator
926                   .registerWorker(new ComplementConsensusThread(this, ap));
927         }
928       }
929     }
930   }
931
932   /**
933    * trigger update of information annotation
934    */
935   @Override
936   public void updateInformation(final AlignmentViewPanel ap)
937   {
938     if (calculator
939             .getRegisteredWorkersOfClass(InformationThread.class) == null)
940     {
941       calculator.registerWorker(new InformationThread(this, ap));
942     }
943
944   }
945
946   // --------START Structure Conservation
947   public void updateStrucConsensus(final AlignmentViewPanel ap)
948   {
949     if (autoCalculateStrucConsensus && strucConsensus == null
950             && alignment.isNucleotide() && alignment.hasRNAStructure())
951     {
952       // secondary structure has been added - so init the consensus line
953       initRNAStructure();
954     }
955
956     // see note in mantis : issue number 8585
957     if (strucConsensus == null || !autoCalculateStrucConsensus)
958     {
959       return;
960     }
961     if (calculator.getRegisteredWorkersOfClass(
962             StrucConsensusThread.class) == null)
963     {
964       calculator.registerWorker(new StrucConsensusThread(this, ap));
965     }
966   }
967
968   public boolean isCalcInProgress()
969   {
970     return calculator.isWorking();
971   }
972
973   @Override
974   public boolean isCalculationInProgress(
975           AlignmentAnnotation alignmentAnnotation)
976   {
977     if (!alignmentAnnotation.autoCalculated)
978     {
979       return false;
980     }
981     if (calculator.workingInvolvedWith(alignmentAnnotation))
982     {
983       // System.err.println("grey out ("+alignmentAnnotation.label+")");
984       return true;
985     }
986     return false;
987   }
988
989   public void setAlignment(AlignmentI align)
990   {
991     this.alignment = align;
992   }
993
994   /**
995    * Clean up references when this viewport is closed
996    */
997   @Override
998   public void dispose()
999   {
1000     /*
1001      * defensively null out references to large objects in case
1002      * this object is not garbage collected (as if!)
1003      */
1004     consensus = null;
1005     complementConsensus = null;
1006     strucConsensus = null;
1007     conservation = null;
1008     quality = null;
1009     groupConsensus = null;
1010     groupConservation = null;
1011     hconsensus = null;
1012     hcomplementConsensus = null;
1013     // colour scheme may hold reference to consensus
1014     residueShading = null;
1015     // TODO remove listeners from changeSupport?
1016     changeSupport = null;
1017     setAlignment(null);
1018   }
1019
1020   @Override
1021   public boolean isClosed()
1022   {
1023     // TODO: check that this isClosed is only true after panel is closed, not
1024     // before it is fully constructed.
1025     return alignment == null;
1026   }
1027
1028   @Override
1029   public AlignCalcManagerI getCalcManager()
1030   {
1031     return calculator;
1032   }
1033
1034   /**
1035    * should conservation rows be shown for groups
1036    */
1037   protected boolean showGroupConservation = false;
1038
1039   /**
1040    * should consensus rows be shown for groups
1041    */
1042   protected boolean showGroupConsensus = false;
1043
1044   /**
1045    * should consensus profile be rendered by default
1046    */
1047   protected boolean showSequenceLogo = false;
1048
1049   /**
1050    * should consensus profile be rendered normalised to row height
1051    */
1052   protected boolean normaliseSequenceLogo = false;
1053
1054   /**
1055    * should consensus histograms be rendered by default
1056    */
1057   protected boolean showConsensusHistogram = true;
1058
1059   /**
1060    * should hmm profile be rendered by default
1061    */
1062   protected boolean showHMMSequenceLogo = false;
1063
1064   /**
1065    * should hmm profile be rendered normalised to row height
1066    */
1067   protected boolean normaliseHMMSequenceLogo = false;
1068
1069   /**
1070    * should information histograms be rendered by default
1071    */
1072   protected boolean showInformationHistogram = true;
1073
1074   /**
1075    * @return the showConsensusProfile
1076    */
1077   @Override
1078   public boolean isShowSequenceLogo()
1079   {
1080     return showSequenceLogo;
1081   }
1082
1083   /**
1084    * @return the showInformationProfile
1085    */
1086   @Override
1087   public boolean isShowHMMSequenceLogo()
1088   {
1089     return showHMMSequenceLogo;
1090   }
1091
1092   /**
1093    * @param showSequenceLogo
1094    *          the new value
1095    */
1096   public void setShowSequenceLogo(boolean showSequenceLogo)
1097   {
1098     if (showSequenceLogo != this.showSequenceLogo)
1099     {
1100       // TODO: decouple settings setting from calculation when refactoring
1101       // annotation update method from alignframe to viewport
1102       this.showSequenceLogo = showSequenceLogo;
1103       calculator.updateAnnotationFor(ConsensusThread.class);
1104       calculator.updateAnnotationFor(ComplementConsensusThread.class);
1105       calculator.updateAnnotationFor(StrucConsensusThread.class);
1106     }
1107     this.showSequenceLogo = showSequenceLogo;
1108   }
1109
1110   public void setShowHMMSequenceLogo(boolean showHMMSequenceLogo)
1111   {
1112     if (showHMMSequenceLogo != this.showHMMSequenceLogo)
1113     {
1114       this.showHMMSequenceLogo = showHMMSequenceLogo;
1115       calculator.updateAnnotationFor(InformationThread.class);
1116     }
1117     this.showHMMSequenceLogo = 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    *          the showInformationHistogram to set
1132    */
1133   public void setShowInformationHistogram(boolean showInformationHistogram)
1134   {
1135     this.showInformationHistogram = showInformationHistogram;
1136   }
1137
1138   /**
1139    * @return the showGroupConservation
1140    */
1141   public boolean isShowGroupConservation()
1142   {
1143     return showGroupConservation;
1144   }
1145
1146   /**
1147    * @param showGroupConservation
1148    *          the showGroupConservation to set
1149    */
1150   public void setShowGroupConservation(boolean showGroupConservation)
1151   {
1152     this.showGroupConservation = showGroupConservation;
1153   }
1154
1155   /**
1156    * @return the showGroupConsensus
1157    */
1158   public boolean isShowGroupConsensus()
1159   {
1160     return showGroupConsensus;
1161   }
1162
1163   /**
1164    * @param showGroupConsensus
1165    *          the showGroupConsensus to set
1166    */
1167   public void setShowGroupConsensus(boolean showGroupConsensus)
1168   {
1169     this.showGroupConsensus = showGroupConsensus;
1170   }
1171
1172   /**
1173    * 
1174    * @return flag to indicate if the consensus histogram should be rendered by
1175    *         default
1176    */
1177   @Override
1178   public boolean isShowConsensusHistogram()
1179   {
1180     return this.showConsensusHistogram;
1181   }
1182
1183   /**
1184    * 
1185    * @return flag to indicate if the information content histogram should be
1186    *         rendered by default
1187    */
1188   @Override
1189   public boolean isShowInformationHistogram()
1190   {
1191     return this.showInformationHistogram;
1192   }
1193
1194   /**
1195    * when set, updateAlignment will always ensure sequences are of equal length
1196    */
1197   private boolean padGaps = false;
1198
1199   /**
1200    * when set, alignment should be reordered according to a newly opened tree
1201    */
1202   public boolean sortByTree = false;
1203
1204   /**
1205    * 
1206    * 
1207    * @return null or the currently selected sequence region
1208    */
1209   @Override
1210   public SequenceGroup getSelectionGroup()
1211   {
1212     return selectionGroup;
1213   }
1214
1215   /**
1216    * Set the selection group for this window. Also sets the current alignment as
1217    * the context for the group, if it does not already have one.
1218    * 
1219    * @param sg
1220    *          - group holding references to sequences in this alignment view
1221    * 
1222    */
1223   @Override
1224   public void setSelectionGroup(SequenceGroup sg)
1225   {
1226     selectionGroup = sg;
1227     if (sg != null && sg.getContext() == null)
1228     {
1229       sg.setContext(alignment);
1230     }
1231   }
1232
1233   public void setHiddenColumns(HiddenColumns hidden)
1234   {
1235     this.alignment.setHiddenColumns(hidden);
1236   }
1237
1238   @Override
1239   public ColumnSelection getColumnSelection()
1240   {
1241     return colSel;
1242   }
1243
1244   @Override
1245   public void setColumnSelection(ColumnSelection colSel)
1246   {
1247     this.colSel = colSel;
1248     if (colSel != null)
1249     {
1250       updateHiddenColumns();
1251     }
1252     isColSelChanged(true);
1253   }
1254
1255   /**
1256    * 
1257    * @return
1258    */
1259   @Override
1260   public Map<SequenceI, SequenceCollectionI> getHiddenRepSequences()
1261   {
1262     return hiddenRepSequences;
1263   }
1264
1265   @Override
1266   public void setHiddenRepSequences(
1267           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
1268   {
1269     this.hiddenRepSequences = hiddenRepSequences;
1270   }
1271
1272   @Override
1273   public boolean hasSelectedColumns()
1274   {
1275     ColumnSelection columnSelection = getColumnSelection();
1276     return columnSelection != null && columnSelection.hasSelectedColumns();
1277   }
1278
1279   @Override
1280   public boolean hasHiddenColumns()
1281   {
1282     return colSel != null
1283             && alignment.getHiddenColumns().hasHiddenColumns();
1284   }
1285
1286   public void updateHiddenColumns()
1287   {
1288     // this method doesn't really do anything now. But - it could, since a
1289     // column Selection could be in the process of modification
1290     // hasHiddenColumns = colSel.hasHiddenColumns();
1291   }
1292
1293   @Override
1294   public boolean hasHiddenRows()
1295   {
1296     return alignment.getHiddenSequences().getSize() > 0;
1297   }
1298
1299   protected SequenceGroup selectionGroup;
1300
1301   public void setSequenceSetId(String newid)
1302   {
1303     if (sequenceSetID != null)
1304     {
1305       System.err.println(
1306               "Warning - overwriting a sequenceSetId for a viewport!");
1307     }
1308     sequenceSetID = new String(newid);
1309   }
1310
1311   @Override
1312   public String getSequenceSetId()
1313   {
1314     if (sequenceSetID == null)
1315     {
1316       sequenceSetID = alignment.hashCode() + "";
1317     }
1318
1319     return sequenceSetID;
1320   }
1321
1322   /**
1323    * unique viewId for synchronizing state (e.g. with stored Jalview Project)
1324    * 
1325    */
1326   protected String viewId = null;
1327
1328   @Override
1329   public String getViewId()
1330   {
1331     if (viewId == null)
1332     {
1333       viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
1334     }
1335     return viewId;
1336   }
1337
1338   public void setIgnoreGapsConsensus(boolean b, AlignmentViewPanel ap)
1339   {
1340     ignoreGapsInConsensusCalculation = b;
1341     if (ap != null)
1342     {
1343       updateConsensus(ap);
1344       if (residueShading != null)
1345       {
1346         residueShading.setThreshold(residueShading.getThreshold(),
1347                 ignoreGapsInConsensusCalculation);
1348       }
1349     }
1350
1351   }
1352
1353   public void setIgnoreBelowBackground(boolean b, AlignmentViewPanel ap)
1354   {
1355     ignoreBelowBackGroundFrequencyCalculation = b;
1356     if (ap != null)
1357     {
1358       updateInformation(ap);
1359     }
1360
1361   }
1362
1363   private long sgrouphash = -1, colselhash = -1;
1364
1365   /**
1366    * checks current SelectionGroup against record of last hash value, and
1367    * updates record.
1368    * 
1369    * @param b
1370    *          update the record of last hash value
1371    * 
1372    * @return true if SelectionGroup changed since last call (when b is true)
1373    */
1374   public boolean isSelectionGroupChanged(boolean b)
1375   {
1376     int hc = (selectionGroup == null || selectionGroup.getSize() == 0) ? -1
1377             : selectionGroup.hashCode();
1378     if (hc != -1 && hc != sgrouphash)
1379     {
1380       if (b)
1381       {
1382         sgrouphash = hc;
1383       }
1384       return true;
1385     }
1386     return false;
1387   }
1388
1389   /**
1390    * checks current colsel against record of last hash value, and optionally
1391    * updates record.
1392    * 
1393    * @param b
1394    *          update the record of last hash value
1395    * @return true if colsel changed since last call (when b is true)
1396    */
1397   public boolean isColSelChanged(boolean b)
1398   {
1399     int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel.hashCode();
1400     if (hc != -1 && hc != colselhash)
1401     {
1402       if (b)
1403       {
1404         colselhash = hc;
1405       }
1406       return true;
1407     }
1408     return false;
1409   }
1410
1411   @Override
1412   public boolean isIgnoreGapsConsensus()
1413   {
1414     return ignoreGapsInConsensusCalculation;
1415   }
1416
1417   @Override
1418   public boolean isIgnoreBelowBackground()
1419   {
1420     return ignoreBelowBackGroundFrequencyCalculation;
1421   }
1422
1423   // property change stuff
1424   // JBPNote Prolly only need this in the applet version.
1425   private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
1426           this);
1427
1428   protected boolean showConservation = true;
1429
1430   protected boolean showQuality = true;
1431
1432   protected boolean showConsensus = true;
1433
1434   protected boolean showOccupancy = true;
1435
1436   private Map<SequenceI, Color> sequenceColours = new HashMap<>();
1437
1438   protected SequenceAnnotationOrder sortAnnotationsBy = null;
1439
1440   protected boolean showAutocalculatedAbove;
1441
1442   /**
1443    * when set, view will scroll to show the highlighted position
1444    */
1445   private boolean followHighlight = true;
1446
1447   /**
1448    * Property change listener for changes in alignment
1449    * 
1450    * @param listener
1451    *          DOCUMENT ME!
1452    */
1453   public void addPropertyChangeListener(
1454           java.beans.PropertyChangeListener listener)
1455   {
1456     changeSupport.addPropertyChangeListener(listener);
1457   }
1458
1459   /**
1460    * DOCUMENT ME!
1461    * 
1462    * @param listener
1463    *          DOCUMENT ME!
1464    */
1465   public void removePropertyChangeListener(
1466           java.beans.PropertyChangeListener listener)
1467   {
1468     changeSupport.removePropertyChangeListener(listener);
1469   }
1470
1471   /**
1472    * Property change listener for changes in alignment
1473    * 
1474    * @param prop
1475    *          DOCUMENT ME!
1476    * @param oldvalue
1477    *          DOCUMENT ME!
1478    * @param newvalue
1479    *          DOCUMENT ME!
1480    */
1481   public void firePropertyChange(String prop, Object oldvalue,
1482           Object newvalue)
1483   {
1484     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
1485   }
1486
1487   // common hide/show column stuff
1488
1489   public void hideSelectedColumns()
1490   {
1491     if (colSel.isEmpty())
1492     {
1493       return;
1494     }
1495
1496     colSel.hideSelectedColumns(alignment);
1497     setSelectionGroup(null);
1498     isColSelChanged(true);
1499   }
1500
1501   public void hideColumns(int start, int end)
1502   {
1503     if (start == end)
1504     {
1505       colSel.hideSelectedColumns(start, alignment.getHiddenColumns());
1506     }
1507     else
1508     {
1509       alignment.getHiddenColumns().hideColumns(start, end);
1510     }
1511     isColSelChanged(true);
1512   }
1513
1514   public void showColumn(int col)
1515   {
1516     alignment.getHiddenColumns().revealHiddenColumns(col, colSel);
1517     isColSelChanged(true);
1518   }
1519
1520   public void showAllHiddenColumns()
1521   {
1522     alignment.getHiddenColumns().revealAllHiddenColumns(colSel);
1523     isColSelChanged(true);
1524   }
1525
1526   // common hide/show seq stuff
1527   public void showAllHiddenSeqs()
1528   {
1529     if (alignment.getHiddenSequences().getSize() > 0)
1530     {
1531       if (selectionGroup == null)
1532       {
1533         selectionGroup = new SequenceGroup();
1534         selectionGroup.setEndRes(alignment.getWidth() - 1);
1535       }
1536       List<SequenceI> tmp = alignment.getHiddenSequences()
1537               .showAll(hiddenRepSequences);
1538       for (SequenceI seq : tmp)
1539       {
1540         selectionGroup.addSequence(seq, false);
1541         setSequenceAnnotationsVisible(seq, true);
1542       }
1543
1544       hiddenRepSequences = null;
1545
1546       firePropertyChange("alignment", null, alignment.getSequences());
1547       // used to set hasHiddenRows/hiddenRepSequences here, after the property
1548       // changed event
1549       sendSelection();
1550     }
1551   }
1552
1553   public void showSequence(int index)
1554   {
1555     List<SequenceI> tmp = alignment.getHiddenSequences().showSequence(index,
1556             hiddenRepSequences);
1557     if (tmp.size() > 0)
1558     {
1559       if (selectionGroup == null)
1560       {
1561         selectionGroup = new SequenceGroup();
1562         selectionGroup.setEndRes(alignment.getWidth() - 1);
1563       }
1564
1565       for (SequenceI seq : tmp)
1566       {
1567         selectionGroup.addSequence(seq, false);
1568         setSequenceAnnotationsVisible(seq, true);
1569       }
1570       firePropertyChange("alignment", null, alignment.getSequences());
1571       sendSelection();
1572     }
1573   }
1574
1575   public void hideAllSelectedSeqs()
1576   {
1577     if (selectionGroup == null || selectionGroup.getSize() < 1)
1578     {
1579       return;
1580     }
1581
1582     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
1583
1584     hideSequence(seqs);
1585
1586     setSelectionGroup(null);
1587   }
1588
1589   public void hideSequence(SequenceI[] seq)
1590   {
1591     if (seq != null)
1592     {
1593       for (int i = 0; i < seq.length; i++)
1594       {
1595         alignment.getHiddenSequences().hideSequence(seq[i]);
1596         setSequenceAnnotationsVisible(seq[i], false);
1597       }
1598       firePropertyChange("alignment", null, alignment.getSequences());
1599     }
1600   }
1601
1602   /**
1603    * Hides the specified sequence, or the sequences it represents
1604    * 
1605    * @param sequence
1606    *          the sequence to hide, or keep as representative
1607    * @param representGroup
1608    *          if true, hide the current selection group except for the
1609    *          representative sequence
1610    */
1611   public void hideSequences(SequenceI sequence, boolean representGroup)
1612   {
1613     if (selectionGroup == null || selectionGroup.getSize() < 1)
1614     {
1615       hideSequence(new SequenceI[] { sequence });
1616       return;
1617     }
1618
1619     if (representGroup)
1620     {
1621       hideRepSequences(sequence, selectionGroup);
1622       setSelectionGroup(null);
1623       return;
1624     }
1625
1626     int gsize = selectionGroup.getSize();
1627     SequenceI[] hseqs = selectionGroup.getSequences()
1628             .toArray(new SequenceI[gsize]);
1629
1630     hideSequence(hseqs);
1631     setSelectionGroup(null);
1632     sendSelection();
1633   }
1634
1635   /**
1636    * Set visibility for any annotations for the given sequence.
1637    * 
1638    * @param sequenceI
1639    */
1640   protected void setSequenceAnnotationsVisible(SequenceI sequenceI,
1641           boolean visible)
1642   {
1643     AlignmentAnnotation[] anns = alignment.getAlignmentAnnotation();
1644     if (anns != null)
1645     {
1646       for (AlignmentAnnotation ann : anns)
1647       {
1648         if (ann.sequenceRef == sequenceI)
1649         {
1650           ann.visible = visible;
1651         }
1652       }
1653     }
1654   }
1655
1656   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
1657   {
1658     int sSize = sg.getSize();
1659     if (sSize < 2)
1660     {
1661       return;
1662     }
1663
1664     if (hiddenRepSequences == null)
1665     {
1666       hiddenRepSequences = new Hashtable<>();
1667     }
1668
1669     hiddenRepSequences.put(repSequence, sg);
1670
1671     // Hide all sequences except the repSequence
1672     SequenceI[] seqs = new SequenceI[sSize - 1];
1673     int index = 0;
1674     for (int i = 0; i < sSize; i++)
1675     {
1676       if (sg.getSequenceAt(i) != repSequence)
1677       {
1678         if (index == sSize - 1)
1679         {
1680           return;
1681         }
1682
1683         seqs[index++] = sg.getSequenceAt(i);
1684       }
1685     }
1686     sg.setSeqrep(repSequence); // note: not done in 2.7applet
1687     sg.setHidereps(true); // note: not done in 2.7applet
1688     hideSequence(seqs);
1689
1690   }
1691
1692   /**
1693    * 
1694    * @return null or the current reference sequence
1695    */
1696   public SequenceI getReferenceSeq()
1697   {
1698     return alignment.getSeqrep();
1699   }
1700
1701   /**
1702    * @param seq
1703    * @return true iff seq is the reference for the alignment
1704    */
1705   public boolean isReferenceSeq(SequenceI seq)
1706   {
1707     return alignment.getSeqrep() == seq;
1708   }
1709
1710   /**
1711    * 
1712    * @param seq
1713    * @return true if there are sequences represented by this sequence that are
1714    *         currently hidden
1715    */
1716   public boolean isHiddenRepSequence(SequenceI seq)
1717   {
1718     return (hiddenRepSequences != null
1719             && hiddenRepSequences.containsKey(seq));
1720   }
1721
1722   /**
1723    * 
1724    * @param seq
1725    * @return null or a sequence group containing the sequences that seq
1726    *         represents
1727    */
1728   public SequenceGroup getRepresentedSequences(SequenceI seq)
1729   {
1730     return (SequenceGroup) (hiddenRepSequences == null ? null
1731             : hiddenRepSequences.get(seq));
1732   }
1733
1734   @Override
1735   public int adjustForHiddenSeqs(int alignmentIndex)
1736   {
1737     return alignment.getHiddenSequences()
1738             .adjustForHiddenSeqs(alignmentIndex);
1739   }
1740
1741   @Override
1742   public void invertColumnSelection()
1743   {
1744     colSel.invertColumnSelection(0, alignment.getWidth(), alignment);
1745   }
1746
1747   @Override
1748   public SequenceI[] getSelectionAsNewSequence()
1749   {
1750     SequenceI[] sequences;
1751     // JBPNote: Need to test jalviewLite.getSelectedSequencesAsAlignmentFrom -
1752     // this was the only caller in the applet for this method
1753     // JBPNote: in applet, this method returned references to the alignment
1754     // sequences, and it did not honour the presence/absence of annotation
1755     // attached to the alignment (probably!)
1756     if (selectionGroup == null || selectionGroup.getSize() == 0)
1757     {
1758       sequences = alignment.getSequencesArray();
1759       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
1760       for (int i = 0; i < sequences.length; i++)
1761       {
1762         // construct new sequence with subset of visible annotation
1763         sequences[i] = new Sequence(sequences[i], annots);
1764       }
1765     }
1766     else
1767     {
1768       sequences = selectionGroup.getSelectionAsNewSequences(alignment);
1769     }
1770
1771     return sequences;
1772   }
1773
1774   @Override
1775   public SequenceI[] getSequenceSelection()
1776   {
1777     SequenceI[] sequences = null;
1778     if (selectionGroup != null)
1779     {
1780       sequences = selectionGroup.getSequencesInOrder(alignment);
1781     }
1782     if (sequences == null)
1783     {
1784       sequences = alignment.getSequencesArray();
1785     }
1786     return sequences;
1787   }
1788
1789   @Override
1790   public CigarArray getViewAsCigars(boolean selectedRegionOnly)
1791   {
1792     return new CigarArray(alignment, alignment.getHiddenColumns(),
1793             (selectedRegionOnly ? selectionGroup : null));
1794   }
1795
1796   @Override
1797   public jalview.datamodel.AlignmentView getAlignmentView(
1798           boolean selectedOnly)
1799   {
1800     return getAlignmentView(selectedOnly, false);
1801   }
1802
1803   @Override
1804   public jalview.datamodel.AlignmentView getAlignmentView(
1805           boolean selectedOnly, boolean markGroups)
1806   {
1807     return new AlignmentView(alignment, alignment.getHiddenColumns(),
1808             selectionGroup, alignment.getHiddenColumns() != null
1809                     && alignment.getHiddenColumns().hasHiddenColumns(),
1810             selectedOnly, markGroups);
1811   }
1812
1813   @Override
1814   public String[] getViewAsString(boolean selectedRegionOnly)
1815   {
1816     return getViewAsString(selectedRegionOnly, true);
1817   }
1818
1819   @Override
1820   public String[] getViewAsString(boolean selectedRegionOnly,
1821           boolean exportHiddenSeqs)
1822   {
1823     String[] selection = null;
1824     SequenceI[] seqs = null;
1825     int i, iSize;
1826     int start = 0, end = 0;
1827     if (selectedRegionOnly && selectionGroup != null)
1828     {
1829       iSize = selectionGroup.getSize();
1830       seqs = selectionGroup.getSequencesInOrder(alignment);
1831       start = selectionGroup.getStartRes();
1832       end = selectionGroup.getEndRes() + 1;
1833     }
1834     else
1835     {
1836       if (hasHiddenRows() && exportHiddenSeqs)
1837       {
1838         AlignmentI fullAlignment = alignment.getHiddenSequences()
1839                 .getFullAlignment();
1840         iSize = fullAlignment.getHeight();
1841         seqs = fullAlignment.getSequencesArray();
1842         end = fullAlignment.getWidth();
1843       }
1844       else
1845       {
1846         iSize = alignment.getHeight();
1847         seqs = alignment.getSequencesArray();
1848         end = alignment.getWidth();
1849       }
1850     }
1851
1852     selection = new String[iSize];
1853     if (alignment.getHiddenColumns() != null
1854             && alignment.getHiddenColumns().hasHiddenColumns())
1855     {
1856       selection = alignment.getHiddenColumns()
1857               .getVisibleSequenceStrings(start, end, seqs);
1858     }
1859     else
1860     {
1861       for (i = 0; i < iSize; i++)
1862       {
1863         selection[i] = seqs[i].getSequenceAsString(start, end);
1864       }
1865
1866     }
1867     return selection;
1868   }
1869
1870   @Override
1871   public List<int[]> getVisibleRegionBoundaries(int min, int max)
1872   {
1873     ArrayList<int[]> regions = new ArrayList<>();
1874     int start = min;
1875     int end = max;
1876
1877     do
1878     {
1879       HiddenColumns hidden = alignment.getHiddenColumns();
1880       if (hidden != null && hidden.hasHiddenColumns())
1881       {
1882         if (start == 0)
1883         {
1884           start = hidden.adjustForHiddenColumns(start);
1885         }
1886
1887         end = hidden.getHiddenBoundaryRight(start);
1888         if (start == end)
1889         {
1890           end = max;
1891         }
1892         if (end > max)
1893         {
1894           end = max;
1895         }
1896       }
1897
1898       regions.add(new int[] { start, end });
1899
1900       if (hidden != null && hidden.hasHiddenColumns())
1901       {
1902         start = hidden.adjustForHiddenColumns(end);
1903         start = hidden.getHiddenBoundaryLeft(start) + 1;
1904       }
1905     } while (end < max);
1906
1907     int[][] startEnd = new int[regions.size()][2];
1908
1909     return regions;
1910   }
1911
1912   @Override
1913   public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(
1914           boolean selectedOnly)
1915   {
1916     ArrayList<AlignmentAnnotation> ala = new ArrayList<>();
1917     AlignmentAnnotation[] aa;
1918     if ((aa = alignment.getAlignmentAnnotation()) != null)
1919     {
1920       for (AlignmentAnnotation annot : aa)
1921       {
1922         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
1923         if (selectedOnly && selectionGroup != null)
1924         {
1925           alignment.getHiddenColumns().makeVisibleAnnotation(
1926                   selectionGroup.getStartRes(), selectionGroup.getEndRes(),
1927                   clone);
1928         }
1929         else
1930         {
1931           alignment.getHiddenColumns().makeVisibleAnnotation(clone);
1932         }
1933         ala.add(clone);
1934       }
1935     }
1936     return ala;
1937   }
1938
1939   @Override
1940   public boolean isPadGaps()
1941   {
1942     return padGaps;
1943   }
1944
1945   @Override
1946   public void setPadGaps(boolean padGaps)
1947   {
1948     this.padGaps = padGaps;
1949   }
1950
1951   /**
1952    * apply any post-edit constraints and trigger any calculations needed after
1953    * an edit has been performed on the alignment
1954    * 
1955    * @param ap
1956    */
1957   @Override
1958   public void alignmentChanged(AlignmentViewPanel ap)
1959   {
1960     if (isPadGaps())
1961     {
1962       alignment.padGaps();
1963     }
1964     if (autoCalculateConsensus)
1965     {
1966       updateConsensus(ap);
1967     }
1968     if (hconsensus != null && autoCalculateConsensus)
1969     {
1970       updateConservation(ap);
1971     }
1972     if (autoCalculateStrucConsensus)
1973     {
1974       updateStrucConsensus(ap);
1975     }
1976     updateInformation(ap);
1977
1978     List<SequenceI> hmmSequences;
1979     hmmSequences = alignment.getHMMConsensusSequences(false);
1980
1981     for (SequenceI seq : hmmSequences)
1982     {
1983       seq.updateHMMMapping();
1984     }
1985
1986     // Reset endRes of groups if beyond alignment width
1987     int alWidth = alignment.getWidth();
1988     List<SequenceGroup> groups = alignment.getGroups();
1989     if (groups != null)
1990     {
1991       for (SequenceGroup sg : groups)
1992       {
1993         if (sg.getEndRes() > alWidth)
1994         {
1995           sg.setEndRes(alWidth - 1);
1996         }
1997       }
1998     }
1999
2000     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
2001     {
2002       selectionGroup.setEndRes(alWidth - 1);
2003     }
2004
2005     updateAllColourSchemes();
2006     calculator.restartWorkers();
2007     // alignment.adjustSequenceAnnotations();
2008   }
2009
2010   /**
2011    * reset scope and do calculations for all applied colourschemes on alignment
2012    */
2013   void updateAllColourSchemes()
2014   {
2015     ResidueShaderI rs = residueShading;
2016     if (rs != null)
2017     {
2018       rs.alignmentChanged(alignment, hiddenRepSequences);
2019
2020       rs.setConsensus(hconsensus);
2021       if (rs.conservationApplied())
2022       {
2023         rs.setConservation(Conservation.calculateConservation("All",
2024                 alignment.getSequences(), 0, alignment.getWidth(), false,
2025                 getConsPercGaps(), false));
2026       }
2027     }
2028
2029     for (SequenceGroup sg : alignment.getGroups())
2030     {
2031       if (sg.cs != null)
2032       {
2033         sg.cs.alignmentChanged(sg, hiddenRepSequences);
2034       }
2035       sg.recalcConservation();
2036     }
2037   }
2038
2039   protected void initAutoAnnotation()
2040   {
2041     // TODO: add menu option action that nulls or creates consensus object
2042     // depending on if the user wants to see the annotation or not in a
2043     // specific alignment
2044
2045     if (hconsensus == null && !isDataset)
2046     {
2047       if (!alignment.isNucleotide())
2048       {
2049         initConservation();
2050         initQuality();
2051       }
2052       else
2053       {
2054         initRNAStructure();
2055       }
2056       consensus = new AlignmentAnnotation("Consensus",
2057               MessageManager.getString("label.consensus_descr"),
2058               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
2059       initConsensus(consensus);
2060
2061       initGapCounts();
2062
2063       initComplementConsensus();
2064     }
2065   }
2066
2067   /**
2068    * If this is a protein alignment and there are mappings to cDNA, adds the
2069    * cDNA consensus annotation and returns true, else returns false.
2070    */
2071   public boolean initComplementConsensus()
2072   {
2073     if (!alignment.isNucleotide())
2074     {
2075       final List<AlignedCodonFrame> codonMappings = alignment
2076               .getCodonFrames();
2077       if (codonMappings != null && !codonMappings.isEmpty())
2078       {
2079         boolean doConsensus = false;
2080         for (AlignedCodonFrame mapping : codonMappings)
2081         {
2082           // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame?
2083           MapList[] mapLists = mapping.getdnaToProt();
2084           // mapLists can be empty if project load has not finished resolving
2085           // seqs
2086           if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3)
2087           {
2088             doConsensus = true;
2089             break;
2090           }
2091         }
2092         if (doConsensus)
2093         {
2094           complementConsensus = new AlignmentAnnotation("cDNA Consensus",
2095                   MessageManager
2096                           .getString("label.complement_consensus_descr"),
2097                   new Annotation[1], 0f, 100f,
2098                   AlignmentAnnotation.BAR_GRAPH);
2099           initConsensus(complementConsensus);
2100           return true;
2101         }
2102       }
2103     }
2104     return false;
2105   }
2106
2107   private void initConsensus(AlignmentAnnotation aa)
2108   {
2109     aa.hasText = true;
2110     aa.autoCalculated = true;
2111
2112     if (showConsensus)
2113     {
2114       alignment.addAnnotation(aa);
2115     }
2116   }
2117
2118   @Override
2119   public void initInformation()
2120   {
2121     for (SequenceI seq : alignment.getHMMConsensusSequences(false))
2122     {
2123       if (!seq.hasHMMAnnotation())
2124       {
2125         AlignmentAnnotation information;
2126         information = new AlignmentAnnotation(seq.getName(),
2127                 MessageManager.getString("label.information_description"),
2128                 new Annotation[1], 0f, 6.52f,
2129                 AlignmentAnnotation.BAR_GRAPH);
2130         information.hasText = true;
2131         information.autoCalculated = true;
2132         information.hasText = true;
2133         information.autoCalculated = false;
2134         information.sequenceRef = seq;
2135         information.setCalcId("HMM annotation");
2136         this.information.add(information);
2137         hinformation.add(new Profiles(new ProfileI[1]));
2138         alignment.addAnnotation(information);
2139         seq.updateHMMMapping();
2140         seq.setHasInfo(true);
2141         seq.addAlignmentAnnotation(information);
2142       }
2143     }
2144
2145   }
2146
2147   // these should be extracted from the view model - style and settings for
2148   // derived annotation
2149   private void initGapCounts()
2150   {
2151     if (showOccupancy)
2152     {
2153       gapcounts = new AlignmentAnnotation("Occupancy",
2154               MessageManager.getString("label.occupancy_descr"),
2155               new Annotation[1], 0f, alignment.getHeight(),
2156               AlignmentAnnotation.BAR_GRAPH);
2157       gapcounts.hasText = true;
2158       gapcounts.autoCalculated = true;
2159       gapcounts.scaleColLabel = true;
2160       gapcounts.graph = AlignmentAnnotation.BAR_GRAPH;
2161
2162       alignment.addAnnotation(gapcounts);
2163     }
2164   }
2165
2166   private void initConservation()
2167   {
2168     if (showConservation)
2169     {
2170       if (conservation == null)
2171       {
2172         conservation = new AlignmentAnnotation("Conservation",
2173                 MessageManager.formatMessage("label.conservation_descr",
2174                         getConsPercGaps()),
2175                 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
2176         conservation.hasText = true;
2177         conservation.autoCalculated = true;
2178         alignment.addAnnotation(conservation);
2179       }
2180     }
2181   }
2182
2183   private void initQuality()
2184   {
2185     if (showQuality)
2186     {
2187       if (quality == null)
2188       {
2189         quality = new AlignmentAnnotation("Quality",
2190                 MessageManager.getString("label.quality_descr"),
2191                 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
2192         quality.hasText = true;
2193         quality.autoCalculated = true;
2194         alignment.addAnnotation(quality);
2195       }
2196     }
2197   }
2198
2199   private void initRNAStructure()
2200   {
2201     if (alignment.hasRNAStructure() && strucConsensus == null)
2202     {
2203       strucConsensus = new AlignmentAnnotation("StrucConsensus",
2204               MessageManager.getString("label.strucconsensus_descr"),
2205               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
2206       strucConsensus.hasText = true;
2207       strucConsensus.autoCalculated = true;
2208
2209       if (showConsensus)
2210       {
2211         alignment.addAnnotation(strucConsensus);
2212       }
2213     }
2214   }
2215
2216   /*
2217    * (non-Javadoc)
2218    * 
2219    * @see jalview.api.AlignViewportI#calcPanelHeight()
2220    */
2221   @Override
2222   public int calcPanelHeight()
2223   {
2224     // setHeight of panels
2225     AlignmentAnnotation[] anns = getAlignment().getAlignmentAnnotation();
2226     int height = 0;
2227     int charHeight = getCharHeight();
2228     if (anns != null)
2229     {
2230       BitSet graphgrp = new BitSet();
2231       for (AlignmentAnnotation aa : anns)
2232       {
2233         if (aa == null)
2234         {
2235           System.err.println("Null annotation row: ignoring.");
2236           continue;
2237         }
2238         if (!aa.visible)
2239         {
2240           continue;
2241         }
2242         if (aa.graphGroup > -1)
2243         {
2244           if (graphgrp.get(aa.graphGroup))
2245           {
2246             continue;
2247           }
2248           else
2249           {
2250             graphgrp.set(aa.graphGroup);
2251           }
2252         }
2253         aa.height = 0;
2254
2255         if (aa.hasText)
2256         {
2257           aa.height += charHeight;
2258         }
2259
2260         if (aa.hasIcons)
2261         {
2262           aa.height += 16;
2263         }
2264
2265         if (aa.graph > 0)
2266         {
2267           aa.height += aa.graphHeight;
2268         }
2269
2270         if (aa.height == 0)
2271         {
2272           aa.height = 20;
2273         }
2274
2275         height += aa.height;
2276       }
2277     }
2278     if (height == 0)
2279     {
2280       // set minimum
2281       height = 20;
2282     }
2283     return height;
2284   }
2285
2286   @Override
2287   public void updateGroupAnnotationSettings(boolean applyGlobalSettings,
2288           boolean preserveNewGroupSettings)
2289   {
2290     boolean updateCalcs = false;
2291     boolean conv = isShowGroupConservation();
2292     boolean cons = isShowGroupConsensus();
2293     boolean showprf = isShowSequenceLogo();
2294     boolean showConsHist = isShowConsensusHistogram();
2295     boolean normLogo = isNormaliseSequenceLogo();
2296     boolean showHMMPrf = isShowHMMSequenceLogo();
2297     boolean showInfoHist = isShowInformationHistogram();
2298     boolean normHMMLogo = isNormaliseHMMSequenceLogo();
2299
2300     /**
2301      * TODO reorder the annotation rows according to group/sequence ordering on
2302      * alignment
2303      */
2304     boolean sortg = true;
2305
2306     // remove old automatic annotation
2307     // add any new annotation
2308
2309     // intersect alignment annotation with alignment groups
2310
2311     AlignmentAnnotation[] aan = alignment.getAlignmentAnnotation();
2312     List<SequenceGroup> oldrfs = new ArrayList<>();
2313     if (aan != null)
2314     {
2315       for (int an = 0; an < aan.length; an++)
2316       {
2317         if (aan[an].autoCalculated && aan[an].groupRef != null)
2318         {
2319           oldrfs.add(aan[an].groupRef);
2320           alignment.deleteAnnotation(aan[an], false);
2321         }
2322       }
2323     }
2324     if (alignment.getGroups() != null)
2325     {
2326       for (SequenceGroup sg : alignment.getGroups())
2327       {
2328         updateCalcs = false;
2329         if (applyGlobalSettings
2330                 || (!preserveNewGroupSettings && !oldrfs.contains(sg)))
2331         {
2332           // set defaults for this group's conservation/consensus
2333           sg.setshowSequenceLogo(showprf);
2334           sg.setShowConsensusHistogram(showConsHist);
2335           sg.setNormaliseSequenceLogo(normLogo);
2336           sg.setshowHMMSequenceLogo(showHMMPrf);
2337           sg.setShowInformationHistogram(showInfoHist);
2338           sg.setNormaliseHMMSequenceLogo(normHMMLogo);
2339         }
2340         if (conv)
2341         {
2342           updateCalcs = true;
2343           alignment.addAnnotation(sg.getConservationRow(), 0);
2344         }
2345         if (cons)
2346         {
2347           updateCalcs = true;
2348           alignment.addAnnotation(sg.getConsensus(), 0);
2349         }
2350         // refresh the annotation rows
2351         if (updateCalcs)
2352         {
2353           sg.recalcConservation();
2354         }
2355       }
2356     }
2357     oldrfs.clear();
2358   }
2359
2360   @Override
2361   public boolean isDisplayReferenceSeq()
2362   {
2363     return alignment.hasSeqrep() && viewStyle.isDisplayReferenceSeq();
2364   }
2365
2366   @Override
2367   public void setDisplayReferenceSeq(boolean displayReferenceSeq)
2368   {
2369     viewStyle.setDisplayReferenceSeq(displayReferenceSeq);
2370   }
2371
2372   @Override
2373   public boolean isColourByReferenceSeq()
2374   {
2375     return alignment.hasSeqrep() && viewStyle.isColourByReferenceSeq();
2376   }
2377
2378   @Override
2379   public Color getSequenceColour(SequenceI seq)
2380   {
2381     Color sqc = sequenceColours.get(seq);
2382     return (sqc == null ? Color.white : sqc);
2383   }
2384
2385   @Override
2386   public void setSequenceColour(SequenceI seq, Color col)
2387   {
2388     if (col == null)
2389     {
2390       sequenceColours.remove(seq);
2391     }
2392     else
2393     {
2394       sequenceColours.put(seq, col);
2395     }
2396   }
2397
2398   @Override
2399   public void updateSequenceIdColours()
2400   {
2401     for (SequenceGroup sg : alignment.getGroups())
2402     {
2403       if (sg.idColour != null)
2404       {
2405         for (SequenceI s : sg.getSequences(getHiddenRepSequences()))
2406         {
2407           sequenceColours.put(s, sg.idColour);
2408         }
2409       }
2410     }
2411   }
2412
2413   @Override
2414   public void clearSequenceColours()
2415   {
2416     sequenceColours.clear();
2417   };
2418
2419   @Override
2420   public AlignViewportI getCodingComplement()
2421   {
2422     return this.codingComplement;
2423   }
2424
2425   /**
2426    * Set this as the (cDna/protein) complement of the given viewport. Also
2427    * ensures the reverse relationship is set on the given viewport.
2428    */
2429   @Override
2430   public void setCodingComplement(AlignViewportI av)
2431   {
2432     if (this == av)
2433     {
2434       System.err.println("Ignoring recursive setCodingComplement request");
2435     }
2436     else
2437     {
2438       this.codingComplement = av;
2439       // avoid infinite recursion!
2440       if (av.getCodingComplement() != this)
2441       {
2442         av.setCodingComplement(this);
2443       }
2444     }
2445   }
2446
2447   @Override
2448   public boolean isNucleotide()
2449   {
2450     return getAlignment() == null ? false : getAlignment().isNucleotide();
2451   }
2452
2453   @Override
2454   public FeaturesDisplayedI getFeaturesDisplayed()
2455   {
2456     return featuresDisplayed;
2457   }
2458
2459   @Override
2460   public void setFeaturesDisplayed(FeaturesDisplayedI featuresDisplayedI)
2461   {
2462     featuresDisplayed = featuresDisplayedI;
2463   }
2464
2465   @Override
2466   public boolean areFeaturesDisplayed()
2467   {
2468     return featuresDisplayed != null
2469             && featuresDisplayed.getRegisteredFeaturesCount() > 0;
2470   }
2471
2472   /**
2473    * set the flag
2474    * 
2475    * @param b
2476    *          features are displayed if true
2477    */
2478   @Override
2479   public void setShowSequenceFeatures(boolean b)
2480   {
2481     viewStyle.setShowSequenceFeatures(b);
2482   }
2483
2484   @Override
2485   public boolean isShowSequenceFeatures()
2486   {
2487     return viewStyle.isShowSequenceFeatures();
2488   }
2489
2490   @Override
2491   public void setShowSequenceFeaturesHeight(boolean selected)
2492   {
2493     viewStyle.setShowSequenceFeaturesHeight(selected);
2494   }
2495
2496   @Override
2497   public boolean isShowSequenceFeaturesHeight()
2498   {
2499     return viewStyle.isShowSequenceFeaturesHeight();
2500   }
2501
2502   @Override
2503   public void setShowAnnotation(boolean b)
2504   {
2505     viewStyle.setShowAnnotation(b);
2506   }
2507
2508   @Override
2509   public boolean isShowAnnotation()
2510   {
2511     return viewStyle.isShowAnnotation();
2512   }
2513
2514   @Override
2515   public boolean isRightAlignIds()
2516   {
2517     return viewStyle.isRightAlignIds();
2518   }
2519
2520   @Override
2521   public void setRightAlignIds(boolean rightAlignIds)
2522   {
2523     viewStyle.setRightAlignIds(rightAlignIds);
2524   }
2525
2526   @Override
2527   public boolean getConservationSelected()
2528   {
2529     return viewStyle.getConservationSelected();
2530   }
2531
2532   @Override
2533   public void setShowBoxes(boolean state)
2534   {
2535     viewStyle.setShowBoxes(state);
2536   }
2537
2538   /**
2539    * @return
2540    * @see jalview.api.ViewStyleI#getTextColour()
2541    */
2542   @Override
2543   public Color getTextColour()
2544   {
2545     return viewStyle.getTextColour();
2546   }
2547
2548   /**
2549    * @return
2550    * @see jalview.api.ViewStyleI#getTextColour2()
2551    */
2552   @Override
2553   public Color getTextColour2()
2554   {
2555     return viewStyle.getTextColour2();
2556   }
2557
2558   /**
2559    * @return
2560    * @see jalview.api.ViewStyleI#getThresholdTextColour()
2561    */
2562   @Override
2563   public int getThresholdTextColour()
2564   {
2565     return viewStyle.getThresholdTextColour();
2566   }
2567
2568   /**
2569    * @return
2570    * @see jalview.api.ViewStyleI#isConservationColourSelected()
2571    */
2572   @Override
2573   public boolean isConservationColourSelected()
2574   {
2575     return viewStyle.isConservationColourSelected();
2576   }
2577
2578   /**
2579    * @return
2580    * @see jalview.api.ViewStyleI#isRenderGaps()
2581    */
2582   @Override
2583   public boolean isRenderGaps()
2584   {
2585     return viewStyle.isRenderGaps();
2586   }
2587
2588   /**
2589    * @return
2590    * @see jalview.api.ViewStyleI#isShowColourText()
2591    */
2592   @Override
2593   public boolean isShowColourText()
2594   {
2595     return viewStyle.isShowColourText();
2596   }
2597
2598   /**
2599    * @param conservationColourSelected
2600    * @see jalview.api.ViewStyleI#setConservationColourSelected(boolean)
2601    */
2602   @Override
2603   public void setConservationColourSelected(
2604           boolean conservationColourSelected)
2605   {
2606     viewStyle.setConservationColourSelected(conservationColourSelected);
2607   }
2608
2609   /**
2610    * @param showColourText
2611    * @see jalview.api.ViewStyleI#setShowColourText(boolean)
2612    */
2613   @Override
2614   public void setShowColourText(boolean showColourText)
2615   {
2616     viewStyle.setShowColourText(showColourText);
2617   }
2618
2619   /**
2620    * @param textColour
2621    * @see jalview.api.ViewStyleI#setTextColour(java.awt.Color)
2622    */
2623   @Override
2624   public void setTextColour(Color textColour)
2625   {
2626     viewStyle.setTextColour(textColour);
2627   }
2628
2629   /**
2630    * @param thresholdTextColour
2631    * @see jalview.api.ViewStyleI#setThresholdTextColour(int)
2632    */
2633   @Override
2634   public void setThresholdTextColour(int thresholdTextColour)
2635   {
2636     viewStyle.setThresholdTextColour(thresholdTextColour);
2637   }
2638
2639   /**
2640    * @param textColour2
2641    * @see jalview.api.ViewStyleI#setTextColour2(java.awt.Color)
2642    */
2643   @Override
2644   public void setTextColour2(Color textColour2)
2645   {
2646     viewStyle.setTextColour2(textColour2);
2647   }
2648
2649   @Override
2650   public ViewStyleI getViewStyle()
2651   {
2652     return new ViewStyle(viewStyle);
2653   }
2654
2655   @Override
2656   public void setViewStyle(ViewStyleI settingsForView)
2657   {
2658     viewStyle = new ViewStyle(settingsForView);
2659     if (residueShading != null)
2660     {
2661       residueShading.setConservationApplied(
2662               settingsForView.isConservationColourSelected());
2663     }
2664   }
2665
2666   @Override
2667   public boolean sameStyle(ViewStyleI them)
2668   {
2669     return viewStyle.sameStyle(them);
2670   }
2671
2672   /**
2673    * @return
2674    * @see jalview.api.ViewStyleI#getIdWidth()
2675    */
2676   @Override
2677   public int getIdWidth()
2678   {
2679     return viewStyle.getIdWidth();
2680   }
2681
2682   /**
2683    * @param i
2684    * @see jalview.api.ViewStyleI#setIdWidth(int)
2685    */
2686   @Override
2687   public void setIdWidth(int i)
2688   {
2689     viewStyle.setIdWidth(i);
2690   }
2691
2692   /**
2693    * @return
2694    * @see jalview.api.ViewStyleI#isCentreColumnLabels()
2695    */
2696   @Override
2697   public boolean isCentreColumnLabels()
2698   {
2699     return viewStyle.isCentreColumnLabels();
2700   }
2701
2702   /**
2703    * @param centreColumnLabels
2704    * @see jalview.api.ViewStyleI#setCentreColumnLabels(boolean)
2705    */
2706   @Override
2707   public void setCentreColumnLabels(boolean centreColumnLabels)
2708   {
2709     viewStyle.setCentreColumnLabels(centreColumnLabels);
2710   }
2711
2712   /**
2713    * @param showdbrefs
2714    * @see jalview.api.ViewStyleI#setShowDBRefs(boolean)
2715    */
2716   @Override
2717   public void setShowDBRefs(boolean showdbrefs)
2718   {
2719     viewStyle.setShowDBRefs(showdbrefs);
2720   }
2721
2722   /**
2723    * @return
2724    * @see jalview.api.ViewStyleI#isShowDBRefs()
2725    */
2726   @Override
2727   public boolean isShowDBRefs()
2728   {
2729     return viewStyle.isShowDBRefs();
2730   }
2731
2732   /**
2733    * @return
2734    * @see jalview.api.ViewStyleI#isShowNPFeats()
2735    */
2736   @Override
2737   public boolean isShowNPFeats()
2738   {
2739     return viewStyle.isShowNPFeats();
2740   }
2741
2742   /**
2743    * @param shownpfeats
2744    * @see jalview.api.ViewStyleI#setShowNPFeats(boolean)
2745    */
2746   @Override
2747   public void setShowNPFeats(boolean shownpfeats)
2748   {
2749     viewStyle.setShowNPFeats(shownpfeats);
2750   }
2751
2752   public abstract StructureSelectionManager getStructureSelectionManager();
2753
2754   /**
2755    * Add one command to the command history list.
2756    * 
2757    * @param command
2758    */
2759   public void addToHistoryList(CommandI command)
2760   {
2761     if (this.historyList != null)
2762     {
2763       this.historyList.push(command);
2764       broadcastCommand(command, false);
2765     }
2766   }
2767
2768   protected void broadcastCommand(CommandI command, boolean undo)
2769   {
2770     getStructureSelectionManager().commandPerformed(command, undo,
2771             getVamsasSource());
2772   }
2773
2774   /**
2775    * Add one command to the command redo list.
2776    * 
2777    * @param command
2778    */
2779   public void addToRedoList(CommandI command)
2780   {
2781     if (this.redoList != null)
2782     {
2783       this.redoList.push(command);
2784     }
2785     broadcastCommand(command, true);
2786   }
2787
2788   /**
2789    * Clear the command redo list.
2790    */
2791   public void clearRedoList()
2792   {
2793     if (this.redoList != null)
2794     {
2795       this.redoList.clear();
2796     }
2797   }
2798
2799   public void setHistoryList(Deque<CommandI> list)
2800   {
2801     this.historyList = list;
2802   }
2803
2804   public Deque<CommandI> getHistoryList()
2805   {
2806     return this.historyList;
2807   }
2808
2809   public void setRedoList(Deque<CommandI> list)
2810   {
2811     this.redoList = list;
2812   }
2813
2814   public Deque<CommandI> getRedoList()
2815   {
2816     return this.redoList;
2817   }
2818
2819   @Override
2820   public VamsasSource getVamsasSource()
2821   {
2822     return this;
2823   }
2824
2825   public SequenceAnnotationOrder getSortAnnotationsBy()
2826   {
2827     return sortAnnotationsBy;
2828   }
2829
2830   public void setSortAnnotationsBy(
2831           SequenceAnnotationOrder sortAnnotationsBy)
2832   {
2833     this.sortAnnotationsBy = sortAnnotationsBy;
2834   }
2835
2836   public boolean isShowAutocalculatedAbove()
2837   {
2838     return showAutocalculatedAbove;
2839   }
2840
2841   public void setShowAutocalculatedAbove(boolean showAutocalculatedAbove)
2842   {
2843     this.showAutocalculatedAbove = showAutocalculatedAbove;
2844   }
2845
2846   @Override
2847   public boolean isScaleProteinAsCdna()
2848   {
2849     return viewStyle.isScaleProteinAsCdna();
2850   }
2851
2852   @Override
2853   public void setScaleProteinAsCdna(boolean b)
2854   {
2855     viewStyle.setScaleProteinAsCdna(b);
2856   }
2857
2858   @Override
2859   public boolean isProteinFontAsCdna()
2860   {
2861     return viewStyle.isProteinFontAsCdna();
2862   }
2863
2864   @Override
2865   public void setProteinFontAsCdna(boolean b)
2866   {
2867     viewStyle.setProteinFontAsCdna(b);
2868   }
2869
2870   /**
2871    * @return true if view should scroll to show the highlighted region of a
2872    *         sequence
2873    * @return
2874    */
2875   @Override
2876   public final boolean isFollowHighlight()
2877   {
2878     return followHighlight;
2879   }
2880
2881   @Override
2882   public final void setFollowHighlight(boolean b)
2883   {
2884     this.followHighlight = b;
2885   }
2886
2887   @Override
2888   public ViewportRanges getRanges()
2889   {
2890     return ranges;
2891   }
2892
2893   /**
2894    * Helper method to populate the SearchResults with the location in the
2895    * complementary alignment to scroll to, in order to match this one.
2896    * 
2897    * @param sr
2898    *          the SearchResults to add to
2899    * @return the offset (below top of visible region) of the matched sequence
2900    */
2901   protected int findComplementScrollTarget(SearchResultsI sr)
2902   {
2903     final AlignViewportI complement = getCodingComplement();
2904     if (complement == null || !complement.isFollowHighlight())
2905     {
2906       return 0;
2907     }
2908     boolean iAmProtein = !getAlignment().isNucleotide();
2909     AlignmentI proteinAlignment = iAmProtein ? getAlignment()
2910             : complement.getAlignment();
2911     if (proteinAlignment == null)
2912     {
2913       return 0;
2914     }
2915     final List<AlignedCodonFrame> mappings = proteinAlignment
2916             .getCodonFrames();
2917
2918     /*
2919      * Heuristic: find the first mapped sequence (if any) with a non-gapped
2920      * residue in the middle column of the visible region. Scroll the
2921      * complementary alignment to line up the corresponding residue.
2922      */
2923     int seqOffset = 0;
2924     SequenceI sequence = null;
2925
2926     /*
2927      * locate 'middle' column (true middle if an odd number visible, left of
2928      * middle if an even number visible)
2929      */
2930     int middleColumn = ranges.getStartRes()
2931             + (ranges.getEndRes() - ranges.getStartRes()) / 2;
2932     final HiddenSequences hiddenSequences = getAlignment()
2933             .getHiddenSequences();
2934
2935     /*
2936      * searching to the bottom of the alignment gives smoother scrolling across
2937      * all gapped visible regions
2938      */
2939     int lastSeq = alignment.getHeight() - 1;
2940     List<AlignedCodonFrame> seqMappings = null;
2941     for (int seqNo = ranges
2942             .getStartSeq(); seqNo < lastSeq; seqNo++, seqOffset++)
2943     {
2944       sequence = getAlignment().getSequenceAt(seqNo);
2945       if (hiddenSequences != null && hiddenSequences.isHidden(sequence))
2946       {
2947         continue;
2948       }
2949       if (Comparison.isGap(sequence.getCharAt(middleColumn)))
2950       {
2951         continue;
2952       }
2953       seqMappings = MappingUtils.findMappingsForSequenceAndOthers(sequence,
2954               mappings,
2955               getCodingComplement().getAlignment().getSequences());
2956       if (!seqMappings.isEmpty())
2957       {
2958         break;
2959       }
2960     }
2961
2962     if (sequence == null || seqMappings == null || seqMappings.isEmpty())
2963     {
2964       /*
2965        * No ungapped mapped sequence in middle column - do nothing
2966        */
2967       return 0;
2968     }
2969     MappingUtils.addSearchResults(sr, sequence,
2970             sequence.findPosition(middleColumn), seqMappings);
2971     return seqOffset;
2972   }
2973
2974   /**
2975    * synthesize a column selection if none exists so it covers the given
2976    * selection group. if wholewidth is false, no column selection is made if the
2977    * selection group covers the whole alignment width.
2978    * 
2979    * @param sg
2980    * @param wholewidth
2981    */
2982   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
2983   {
2984     int sgs, sge;
2985     if (sg != null && (sgs = sg.getStartRes()) >= 0
2986             && sg.getStartRes() <= (sge = sg.getEndRes())
2987             && !this.hasSelectedColumns())
2988     {
2989       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
2990       {
2991         // do nothing
2992         return;
2993       }
2994       if (colSel == null)
2995       {
2996         colSel = new ColumnSelection();
2997       }
2998       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
2999       {
3000         colSel.addElement(cspos);
3001       }
3002     }
3003   }
3004
3005   /**
3006    * hold status of current selection group - defined on alignment or not.
3007    */
3008   private boolean selectionIsDefinedGroup = false;
3009
3010   @Override
3011   public boolean isSelectionDefinedGroup()
3012   {
3013     if (selectionGroup == null)
3014     {
3015       return false;
3016     }
3017     if (isSelectionGroupChanged(true))
3018     {
3019       selectionIsDefinedGroup = false;
3020       List<SequenceGroup> gps = alignment.getGroups();
3021       if (gps == null || gps.size() == 0)
3022       {
3023         selectionIsDefinedGroup = false;
3024       }
3025       else
3026       {
3027         selectionIsDefinedGroup = gps.contains(selectionGroup);
3028       }
3029     }
3030     return selectionGroup.isDefined() || selectionIsDefinedGroup;
3031   }
3032
3033   /**
3034    * null, or currently highlighted results on this view
3035    */
3036   private SearchResultsI searchResults = null;
3037
3038   @Override
3039   public boolean hasSearchResults()
3040   {
3041     return searchResults != null;
3042   }
3043
3044   @Override
3045   public void setSearchResults(SearchResultsI results)
3046   {
3047     searchResults = results;
3048   }
3049
3050   @Override
3051   public SearchResultsI getSearchResults()
3052   {
3053     return searchResults;
3054   }
3055
3056
3057
3058 }