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