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