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