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