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