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