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