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