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