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