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