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