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