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