JAL-845 SplitFrame for "show product" and after aligning from SplitFrame
[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   @Override
1058   public String getViewId()
1059   {
1060     if (viewId == null)
1061     {
1062       viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
1063     }
1064     return viewId;
1065   }
1066
1067   public void setIgnoreGapsConsensus(boolean b, AlignmentViewPanel ap)
1068   {
1069     ignoreGapsInConsensusCalculation = b;
1070     if (ap != null)
1071     {
1072       updateConsensus(ap);
1073       if (globalColourScheme != null)
1074       {
1075         globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
1076                 ignoreGapsInConsensusCalculation);
1077       }
1078     }
1079
1080   }
1081
1082   private long sgrouphash = -1, colselhash = -1;
1083
1084   /**
1085    * checks current SelectionGroup against record of last hash value, and
1086    * updates record.
1087    * 
1088    * @param b
1089    *          update the record of last hash value
1090    * 
1091    * @return true if SelectionGroup changed since last call (when b is true)
1092    */
1093   public boolean isSelectionGroupChanged(boolean b)
1094   {
1095     int hc = (selectionGroup == null || selectionGroup.getSize() == 0) ? -1
1096             : selectionGroup.hashCode();
1097     if (hc != -1 && hc != sgrouphash)
1098     {
1099       if (b)
1100       {
1101         sgrouphash = hc;
1102       }
1103       return true;
1104     }
1105     return false;
1106   }
1107
1108   /**
1109    * checks current colsel against record of last hash value, and optionally
1110    * updates record.
1111    * 
1112    * @param b
1113    *          update the record of last hash value
1114    * @return true if colsel changed since last call (when b is true)
1115    */
1116   public boolean isColSelChanged(boolean b)
1117   {
1118     int hc = (colSel == null || colSel.size() == 0) ? -1 : colSel
1119             .hashCode();
1120     if (hc != -1 && hc != colselhash)
1121     {
1122       if (b)
1123       {
1124         colselhash = hc;
1125       }
1126       return true;
1127     }
1128     return false;
1129   }
1130
1131   @Override
1132   public boolean isIgnoreGapsConsensus()
1133   {
1134     return ignoreGapsInConsensusCalculation;
1135   }
1136
1137   // / property change stuff
1138
1139   // JBPNote Prolly only need this in the applet version.
1140   private final java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
1141           this);
1142
1143   protected boolean showConservation = true;
1144
1145   protected boolean showQuality = true;
1146
1147   protected boolean showConsensus = true;
1148
1149   private Map<SequenceI, Color> sequenceColours = new HashMap<SequenceI, Color>();
1150
1151   /**
1152    * Property change listener for changes in alignment
1153    * 
1154    * @param listener
1155    *          DOCUMENT ME!
1156    */
1157   public void addPropertyChangeListener(
1158           java.beans.PropertyChangeListener listener)
1159   {
1160     changeSupport.addPropertyChangeListener(listener);
1161   }
1162
1163   /**
1164    * DOCUMENT ME!
1165    * 
1166    * @param listener
1167    *          DOCUMENT ME!
1168    */
1169   public void removePropertyChangeListener(
1170           java.beans.PropertyChangeListener listener)
1171   {
1172     changeSupport.removePropertyChangeListener(listener);
1173   }
1174
1175   /**
1176    * Property change listener for changes in alignment
1177    * 
1178    * @param prop
1179    *          DOCUMENT ME!
1180    * @param oldvalue
1181    *          DOCUMENT ME!
1182    * @param newvalue
1183    *          DOCUMENT ME!
1184    */
1185   public void firePropertyChange(String prop, Object oldvalue,
1186           Object newvalue)
1187   {
1188     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
1189   }
1190
1191   // common hide/show column stuff
1192
1193   public void hideSelectedColumns()
1194   {
1195     if (colSel.size() < 1)
1196     {
1197       return;
1198     }
1199
1200     colSel.hideSelectedColumns();
1201     setSelectionGroup(null);
1202
1203   }
1204
1205   public void hideColumns(int start, int end)
1206   {
1207     if (start == end)
1208     {
1209       colSel.hideColumns(start);
1210     }
1211     else
1212     {
1213       colSel.hideColumns(start, end);
1214     }
1215   }
1216
1217   public void showColumn(int col)
1218   {
1219     colSel.revealHiddenColumns(col);
1220
1221   }
1222
1223   public void showAllHiddenColumns()
1224   {
1225     colSel.revealAllHiddenColumns();
1226   }
1227
1228   // common hide/show seq stuff
1229   public void showAllHiddenSeqs()
1230   {
1231     if (alignment.getHiddenSequences().getSize() > 0)
1232     {
1233       if (selectionGroup == null)
1234       {
1235         selectionGroup = new SequenceGroup();
1236         selectionGroup.setEndRes(alignment.getWidth() - 1);
1237       }
1238       List<SequenceI> tmp = alignment.getHiddenSequences().showAll(
1239               hiddenRepSequences);
1240       for (SequenceI seq : tmp)
1241       {
1242         selectionGroup.addSequence(seq, false);
1243         setSequenceAnnotationsVisible(seq, true);
1244       }
1245
1246       hasHiddenRows = false;
1247       hiddenRepSequences = null;
1248
1249       firePropertyChange("alignment", null, alignment.getSequences());
1250       // used to set hasHiddenRows/hiddenRepSequences here, after the property
1251       // changed event
1252       sendSelection();
1253     }
1254   }
1255
1256   public void showSequence(int index)
1257   {
1258     List<SequenceI> tmp = alignment.getHiddenSequences().showSequence(
1259             index,
1260             hiddenRepSequences);
1261     if (tmp.size() > 0)
1262     {
1263       if (selectionGroup == null)
1264       {
1265         selectionGroup = new SequenceGroup();
1266         selectionGroup.setEndRes(alignment.getWidth() - 1);
1267       }
1268
1269       for (SequenceI seq : tmp)
1270       {
1271         selectionGroup.addSequence(seq, false);
1272         setSequenceAnnotationsVisible(seq, true);
1273       }
1274       // JBPNote: refactor: only update flag if we modified visiblity (used to
1275       // do this regardless)
1276       if (alignment.getHiddenSequences().getSize() < 1)
1277       {
1278         hasHiddenRows = false;
1279       }
1280       firePropertyChange("alignment", null, alignment.getSequences());
1281       sendSelection();
1282     }
1283   }
1284
1285   public void hideAllSelectedSeqs()
1286   {
1287     if (selectionGroup == null || selectionGroup.getSize() < 1)
1288     {
1289       return;
1290     }
1291
1292     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
1293
1294     hideSequence(seqs);
1295
1296     setSelectionGroup(null);
1297   }
1298
1299   public void hideSequence(SequenceI[] seq)
1300   {
1301     if (seq != null)
1302     {
1303       for (int i = 0; i < seq.length; i++)
1304       {
1305         alignment.getHiddenSequences().hideSequence(seq[i]);
1306         setSequenceAnnotationsVisible(seq[i], false);
1307       }
1308       hasHiddenRows = true;
1309       firePropertyChange("alignment", null, alignment.getSequences());
1310     }
1311   }
1312
1313   /**
1314    * Set visibility for any annotations for the given sequence.
1315    * 
1316    * @param sequenceI
1317    */
1318   protected void setSequenceAnnotationsVisible(SequenceI sequenceI,
1319           boolean visible)
1320   {
1321     for (AlignmentAnnotation ann : alignment.getAlignmentAnnotation())
1322     {
1323       if (ann.sequenceRef == sequenceI)
1324       {
1325         ann.visible = visible;
1326       }
1327     }
1328   }
1329
1330   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
1331   {
1332     int sSize = sg.getSize();
1333     if (sSize < 2)
1334     {
1335       return;
1336     }
1337
1338     if (hiddenRepSequences == null)
1339     {
1340       hiddenRepSequences = new Hashtable();
1341     }
1342
1343     hiddenRepSequences.put(repSequence, sg);
1344
1345     // Hide all sequences except the repSequence
1346     SequenceI[] seqs = new SequenceI[sSize - 1];
1347     int index = 0;
1348     for (int i = 0; i < sSize; i++)
1349     {
1350       if (sg.getSequenceAt(i) != repSequence)
1351       {
1352         if (index == sSize - 1)
1353         {
1354           return;
1355         }
1356
1357         seqs[index++] = sg.getSequenceAt(i);
1358       }
1359     }
1360     sg.setSeqrep(repSequence); // note: not done in 2.7applet
1361     sg.setHidereps(true); // note: not done in 2.7applet
1362     hideSequence(seqs);
1363
1364   }
1365
1366   public boolean isHiddenRepSequence(SequenceI seq)
1367   {
1368     return alignment.getSeqrep()==seq || (hiddenRepSequences != null
1369             && hiddenRepSequences.containsKey(seq));
1370   }
1371
1372   public SequenceGroup getRepresentedSequences(SequenceI seq)
1373   {
1374     return (SequenceGroup) (hiddenRepSequences == null ? null
1375             : hiddenRepSequences.get(seq));
1376   }
1377
1378   @Override
1379   public int adjustForHiddenSeqs(int alignmentIndex)
1380   {
1381     return alignment.getHiddenSequences().adjustForHiddenSeqs(
1382             alignmentIndex);
1383   }
1384
1385   @Override
1386   public void invertColumnSelection()
1387   {
1388     colSel.invertColumnSelection(0, alignment.getWidth());
1389   }
1390
1391
1392   @Override
1393   public SequenceI[] getSelectionAsNewSequence()
1394   {
1395     SequenceI[] sequences;
1396     // JBPNote: Need to test jalviewLite.getSelectedSequencesAsAlignmentFrom -
1397     // this was the only caller in the applet for this method
1398     // JBPNote: in applet, this method returned references to the alignment
1399     // sequences, and it did not honour the presence/absence of annotation
1400     // attached to the alignment (probably!)
1401     if (selectionGroup == null || selectionGroup.getSize() == 0)
1402     {
1403       sequences = alignment.getSequencesArray();
1404       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
1405       for (int i = 0; i < sequences.length; i++)
1406       {
1407         // construct new sequence with subset of visible annotation
1408         sequences[i] = new Sequence(sequences[i], annots);
1409       }
1410     }
1411     else
1412     {
1413       sequences = selectionGroup.getSelectionAsNewSequences(alignment);
1414     }
1415
1416     return sequences;
1417   }
1418
1419
1420   @Override
1421   public SequenceI[] getSequenceSelection()
1422   {
1423     SequenceI[] sequences = null;
1424     if (selectionGroup != null)
1425     {
1426       sequences = selectionGroup.getSequencesInOrder(alignment);
1427     }
1428     if (sequences == null)
1429     {
1430       sequences = alignment.getSequencesArray();
1431     }
1432     return sequences;
1433   }
1434
1435
1436   @Override
1437   public CigarArray getViewAsCigars(
1438           boolean selectedRegionOnly)
1439   {
1440     return new CigarArray(alignment, colSel,
1441             (selectedRegionOnly ? selectionGroup : null));
1442   }
1443
1444
1445   @Override
1446   public jalview.datamodel.AlignmentView getAlignmentView(
1447           boolean selectedOnly)
1448   {
1449     return getAlignmentView(selectedOnly, false);
1450   }
1451
1452
1453   @Override
1454   public jalview.datamodel.AlignmentView getAlignmentView(
1455           boolean selectedOnly, boolean markGroups)
1456   {
1457     return new AlignmentView(alignment, colSel, selectionGroup,
1458             colSel != null && colSel.hasHiddenColumns(), selectedOnly,
1459             markGroups);
1460   }
1461
1462
1463   @Override
1464   public String[] getViewAsString(boolean selectedRegionOnly)
1465   {
1466     String[] selection = null;
1467     SequenceI[] seqs = null;
1468     int i, iSize;
1469     int start = 0, end = 0;
1470     if (selectedRegionOnly && selectionGroup != null)
1471     {
1472       iSize = selectionGroup.getSize();
1473       seqs = selectionGroup.getSequencesInOrder(alignment);
1474       start = selectionGroup.getStartRes();
1475       end = selectionGroup.getEndRes() + 1;
1476     }
1477     else
1478     {
1479       iSize = alignment.getHeight();
1480       seqs = alignment.getSequencesArray();
1481       end = alignment.getWidth();
1482     }
1483
1484     selection = new String[iSize];
1485     if (colSel != null && colSel.hasHiddenColumns())
1486     {
1487       selection = colSel.getVisibleSequenceStrings(start, end, seqs);
1488     }
1489     else
1490     {
1491       for (i = 0; i < iSize; i++)
1492       {
1493         selection[i] = seqs[i].getSequenceAsString(start, end);
1494       }
1495
1496     }
1497     return selection;
1498   }
1499
1500
1501   @Override
1502   public List<int[]> getVisibleRegionBoundaries(int min, int max)
1503   {
1504     ArrayList<int[]> regions = new ArrayList<int[]>();
1505     int start = min;
1506     int end = max;
1507
1508     do
1509     {
1510       if (colSel != null && colSel.hasHiddenColumns())
1511       {
1512         if (start == 0)
1513         {
1514           start = colSel.adjustForHiddenColumns(start);
1515         }
1516
1517         end = colSel.getHiddenBoundaryRight(start);
1518         if (start == end)
1519         {
1520           end = max;
1521         }
1522         if (end > max)
1523         {
1524           end = max;
1525         }
1526       }
1527
1528       regions.add(new int[]
1529       { start, end });
1530
1531       if (colSel != null && colSel.hasHiddenColumns())
1532       {
1533         start = colSel.adjustForHiddenColumns(end);
1534         start = colSel.getHiddenBoundaryLeft(start) + 1;
1535       }
1536     } while (end < max);
1537
1538     int[][] startEnd = new int[regions.size()][2];
1539
1540     return regions;
1541   }
1542
1543   @Override
1544   public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(boolean selectedOnly)
1545   {
1546     ArrayList<AlignmentAnnotation> ala = new ArrayList<AlignmentAnnotation>();
1547     AlignmentAnnotation[] aa;
1548     if ((aa=alignment.getAlignmentAnnotation())!=null)
1549     {
1550       for (AlignmentAnnotation annot:aa)
1551       {
1552         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
1553         if (selectedOnly && selectionGroup!=null)
1554         {
1555           colSel.makeVisibleAnnotation(selectionGroup.getStartRes(), selectionGroup.getEndRes(),clone);
1556         } else {
1557           colSel.makeVisibleAnnotation(clone);
1558         }
1559         ala.add(clone);
1560       }
1561     }
1562     return ala;
1563   }
1564
1565
1566   @Override
1567   public boolean isPadGaps()
1568   {
1569     return padGaps;
1570   }
1571
1572
1573   @Override
1574   public void setPadGaps(boolean padGaps)
1575   {
1576     this.padGaps = padGaps;
1577   }
1578
1579   /**
1580    * apply any post-edit constraints and trigger any calculations needed after
1581    * an edit has been performed on the alignment
1582    * 
1583    * @param ap
1584    */
1585   @Override
1586   public void alignmentChanged(AlignmentViewPanel ap)
1587   {
1588     if (isPadGaps())
1589     {
1590       alignment.padGaps();
1591     }
1592     if (autoCalculateConsensus)
1593     {
1594       updateConsensus(ap);
1595     }
1596     if (hconsensus != null && autoCalculateConsensus)
1597     {
1598       updateConservation(ap);
1599     }
1600     if (autoCalculateStrucConsensus)
1601     {
1602       updateStrucConsensus(ap);
1603     }
1604
1605     // Reset endRes of groups if beyond alignment width
1606     int alWidth = alignment.getWidth();
1607     List<SequenceGroup> groups = alignment.getGroups();
1608     if (groups != null)
1609     {
1610       for (SequenceGroup sg : groups)
1611       {
1612         if (sg.getEndRes() > alWidth)
1613         {
1614           sg.setEndRes(alWidth - 1);
1615         }
1616       }
1617     }
1618
1619     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1620     {
1621       selectionGroup.setEndRes(alWidth - 1);
1622     }
1623
1624     resetAllColourSchemes();
1625     calculator.restartWorkers();
1626     // alignment.adjustSequenceAnnotations();
1627   }
1628
1629   /**
1630    * reset scope and do calculations for all applied colourschemes on alignment
1631    */
1632   void resetAllColourSchemes()
1633   {
1634     ColourSchemeI cs = globalColourScheme;
1635     if (cs != null)
1636     {
1637       cs.alignmentChanged(alignment, hiddenRepSequences);
1638
1639       cs.setConsensus(hconsensus);
1640       if (cs.conservationApplied())
1641       {
1642         cs.setConservation(Conservation.calculateConservation("All",
1643                 ResidueProperties.propHash, 3, alignment.getSequences(), 0,
1644                 alignment.getWidth(), false, getConsPercGaps(), false));
1645       }
1646     }
1647
1648     for (SequenceGroup sg : alignment.getGroups())
1649     {
1650       if (sg.cs != null)
1651       {
1652         sg.cs.alignmentChanged(sg, hiddenRepSequences);
1653       }
1654       sg.recalcConservation();
1655     }
1656   }
1657
1658   protected void initAutoAnnotation()
1659   {
1660     // TODO: add menu option action that nulls or creates consensus object
1661     // depending on if the user wants to see the annotation or not in a
1662     // specific alignment
1663
1664     if (hconsensus == null && !isDataset)
1665     {
1666       if (!alignment.isNucleotide())
1667       {
1668         initConservation();
1669         initQuality();
1670       }
1671       else
1672       {
1673         initRNAStructure();
1674       }
1675       initConsensus();
1676     }
1677   }
1678
1679   private void initConsensus()
1680   {
1681
1682     consensus = new AlignmentAnnotation("Consensus", "PID",
1683             new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
1684     consensus.hasText = true;
1685     consensus.autoCalculated = true;
1686
1687     if (showConsensus)
1688     {
1689       alignment.addAnnotation(consensus);
1690     }
1691   }
1692
1693   private void initConservation()
1694   {
1695     if (showConservation)
1696     {
1697       if (conservation == null)
1698       {
1699         conservation = new AlignmentAnnotation("Conservation",
1700                 "Conservation of total alignment less than "
1701                         + getConsPercGaps() + "% gaps", new Annotation[1],
1702                 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
1703         conservation.hasText = true;
1704         conservation.autoCalculated = true;
1705         alignment.addAnnotation(conservation);
1706       }
1707     }
1708   }
1709
1710   private void initQuality()
1711   {
1712     if (showQuality)
1713     {
1714       if (quality == null)
1715       {
1716         quality = new AlignmentAnnotation("Quality",
1717                 "Alignment Quality based on Blosum62 scores",
1718                 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
1719         quality.hasText = true;
1720         quality.autoCalculated = true;
1721         alignment.addAnnotation(quality);
1722       }
1723     }
1724   }
1725
1726   private void initRNAStructure()
1727   {
1728     if (alignment.hasRNAStructure() && strucConsensus == null)
1729     {
1730       strucConsensus = new AlignmentAnnotation("StrucConsensus", "PID",
1731               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
1732       strucConsensus.hasText = true;
1733       strucConsensus.autoCalculated = true;
1734
1735       if (showConsensus)
1736       {
1737         alignment.addAnnotation(strucConsensus);
1738       }
1739     }
1740   }
1741
1742   /*
1743    * (non-Javadoc)
1744    * 
1745    * @see jalview.api.AlignViewportI#calcPanelHeight()
1746    */
1747   @Override
1748   public int calcPanelHeight()
1749   {
1750     // setHeight of panels
1751     AlignmentAnnotation[] aa = getAlignment().getAlignmentAnnotation();
1752     int height = 0;
1753     int charHeight = getCharHeight();
1754     if (aa != null)
1755     {
1756       BitSet graphgrp = new BitSet();
1757       for (int i = 0; i < aa.length; i++)
1758       {
1759         if (aa[i] == null)
1760         {
1761           System.err.println("Null annotation row: ignoring.");
1762           continue;
1763         }
1764         if (!aa[i].visible)
1765         {
1766           continue;
1767         }
1768         if (aa[i].graphGroup > -1)
1769         {
1770           if (graphgrp.get(aa[i].graphGroup))
1771           {
1772             continue;
1773           }
1774           else
1775           {
1776             graphgrp.set(aa[i].graphGroup);
1777           }
1778         }
1779         aa[i].height = 0;
1780
1781         if (aa[i].hasText)
1782         {
1783           aa[i].height += charHeight;
1784         }
1785
1786         if (aa[i].hasIcons)
1787         {
1788           aa[i].height += 16;
1789         }
1790
1791         if (aa[i].graph > 0)
1792         {
1793           aa[i].height += aa[i].graphHeight;
1794         }
1795
1796         if (aa[i].height == 0)
1797         {
1798           aa[i].height = 20;
1799         }
1800
1801         height += aa[i].height;
1802       }
1803     }
1804     if (height == 0)
1805     {
1806       // set minimum
1807       height = 20;
1808     }
1809     return height;
1810   }
1811
1812   @Override
1813   public void updateGroupAnnotationSettings(boolean applyGlobalSettings,
1814           boolean preserveNewGroupSettings)
1815   {
1816     boolean updateCalcs = false;
1817     boolean conv = isShowGroupConservation();
1818     boolean cons = isShowGroupConsensus();
1819     boolean showprf = isShowSequenceLogo();
1820     boolean showConsHist = isShowConsensusHistogram();
1821     boolean normLogo = isNormaliseSequenceLogo();
1822
1823     /**
1824      * TODO reorder the annotation rows according to group/sequence ordering on
1825      * alignment
1826      */
1827     boolean sortg = true;
1828
1829     // remove old automatic annotation
1830     // add any new annotation
1831
1832     // intersect alignment annotation with alignment groups
1833
1834     AlignmentAnnotation[] aan = alignment.getAlignmentAnnotation();
1835     List<SequenceGroup> oldrfs = new ArrayList<SequenceGroup>();
1836     if (aan != null)
1837     {
1838       for (int an = 0; an < aan.length; an++)
1839       {
1840         if (aan[an].autoCalculated && aan[an].groupRef != null)
1841         {
1842           oldrfs.add(aan[an].groupRef);
1843           alignment.deleteAnnotation(aan[an], false);
1844         }
1845       }
1846     }
1847     if (alignment.getGroups() != null)
1848     {
1849       for (SequenceGroup sg : alignment.getGroups())
1850       {
1851         updateCalcs = false;
1852         if (applyGlobalSettings
1853                 || (!preserveNewGroupSettings && !oldrfs.contains(sg)))
1854         {
1855           // set defaults for this group's conservation/consensus
1856           sg.setshowSequenceLogo(showprf);
1857           sg.setShowConsensusHistogram(showConsHist);
1858           sg.setNormaliseSequenceLogo(normLogo);
1859         }
1860         if (conv)
1861         {
1862           updateCalcs = true;
1863           alignment.addAnnotation(sg.getConservationRow(), 0);
1864         }
1865         if (cons)
1866         {
1867           updateCalcs = true;
1868           alignment.addAnnotation(sg.getConsensus(), 0);
1869         }
1870         // refresh the annotation rows
1871         if (updateCalcs)
1872         {
1873           sg.recalcConservation();
1874         }
1875       }
1876     }
1877     oldrfs.clear();
1878   }
1879   @Override
1880   public boolean isDisplayReferenceSeq()
1881   {
1882     return alignment.hasSeqrep() && viewStyle.isDisplayReferenceSeq();
1883   }
1884
1885   @Override
1886   public void setDisplayReferenceSeq(boolean displayReferenceSeq)
1887   {
1888     viewStyle.setDisplayReferenceSeq(displayReferenceSeq);
1889   }
1890
1891   @Override
1892   public boolean isColourByReferenceSeq()
1893   {
1894     return alignment.hasSeqrep() && viewStyle.isColourByReferenceSeq();
1895   }
1896
1897   @Override
1898   public Color getSequenceColour(SequenceI seq)
1899   {
1900     Color sqc = sequenceColours.get(seq);
1901     return (sqc == null ? Color.white : sqc);
1902   }
1903
1904   @Override
1905   public void setSequenceColour(SequenceI seq, Color col)
1906   {
1907     if (col == null)
1908     {
1909       sequenceColours.remove(seq);
1910     }
1911     else
1912     {
1913       sequenceColours.put(seq, col);
1914     }
1915   }
1916
1917   @Override
1918   public void updateSequenceIdColours()
1919   {
1920     for (SequenceGroup sg : alignment.getGroups())
1921     {
1922       if (sg.idColour != null)
1923       {
1924         for (SequenceI s : sg.getSequences(getHiddenRepSequences()))
1925         {
1926           sequenceColours.put(s, sg.idColour);
1927         }
1928       }
1929     }
1930   }
1931
1932   @Override
1933   public void clearSequenceColours()
1934   {
1935     sequenceColours.clear();
1936   };
1937
1938   @Override
1939   public AlignViewportI getCodingComplement()
1940   {
1941     return this.codingComplement;
1942   }
1943
1944   /**
1945    * Set this as the (cDna/protein) complement of the given viewport. Also
1946    * ensures the reverse relationship is set on the given viewport.
1947    */
1948   @Override
1949   public void setCodingComplement(AlignViewportI av)
1950   {
1951     if (this == av)
1952     {
1953       System.err.println("Ignoring recursive setCodingComplement request");
1954     }
1955     else
1956     {
1957       this.codingComplement = av;
1958       // avoid infinite recursion!
1959       if (av.getCodingComplement() != this)
1960       {
1961         av.setCodingComplement(this);
1962       }
1963     }
1964   }
1965
1966   @Override
1967   public boolean isNucleotide()
1968   {
1969     return getAlignment() == null ? false : getAlignment().isNucleotide();
1970   }
1971
1972   @Override
1973   public FeaturesDisplayedI getFeaturesDisplayed()
1974   {
1975     return featuresDisplayed;
1976   }
1977
1978   @Override
1979   public void setFeaturesDisplayed(FeaturesDisplayedI featuresDisplayedI)
1980   {
1981     featuresDisplayed = featuresDisplayedI;
1982   }
1983
1984   @Override
1985   public boolean areFeaturesDisplayed()
1986   {
1987     return featuresDisplayed != null && featuresDisplayed.getRegisterdFeaturesCount()>0;
1988   }
1989
1990   /**
1991    * set the flag
1992    * 
1993    * @param b
1994    *          features are displayed if true
1995    */
1996   @Override
1997   public void setShowSequenceFeatures(boolean b)
1998   {
1999     viewStyle.setShowSequenceFeatures(b);
2000   }
2001   @Override
2002   public boolean isShowSequenceFeatures()
2003   {
2004     return viewStyle.isShowSequenceFeatures();
2005   }
2006
2007   @Override
2008   public void setShowSequenceFeaturesHeight(boolean selected)
2009   {
2010     viewStyle.setShowSeqFeaturesHeight(selected);
2011   }
2012
2013   @Override
2014   public boolean isShowSequenceFeaturesHeight()
2015   {
2016     return viewStyle.isShowSequenceFeaturesHeight();
2017   }
2018
2019
2020
2021   @Override
2022   public void setShowAnnotation(boolean b)
2023   {
2024     viewStyle.setShowAnnotation(b);
2025   }
2026
2027   @Override
2028   public boolean isShowAnnotation()
2029   {
2030     return viewStyle.isShowAnnotation();
2031   }
2032
2033   @Override
2034   public boolean isRightAlignIds()
2035   {
2036     return viewStyle.isRightAlignIds();
2037   }
2038
2039   @Override
2040   public void setRightAlignIds(boolean rightAlignIds)
2041   {
2042     viewStyle.setRightAlignIds(rightAlignIds);
2043   }
2044
2045   @Override
2046   public boolean getConservationSelected()
2047   {
2048     return viewStyle.getConservationSelected();
2049   }
2050
2051   @Override
2052   public void setShowBoxes(boolean state)
2053   {
2054     viewStyle.setShowBoxes(state);
2055   }
2056
2057   /**
2058    * @return
2059    * @see jalview.api.ViewStyleI#getTextColour()
2060    */
2061   public Color getTextColour()
2062   {
2063     return viewStyle.getTextColour();
2064   }
2065
2066   /**
2067    * @return
2068    * @see jalview.api.ViewStyleI#getTextColour2()
2069    */
2070   public Color getTextColour2()
2071   {
2072     return viewStyle.getTextColour2();
2073   }
2074
2075   /**
2076    * @return
2077    * @see jalview.api.ViewStyleI#getThresholdTextColour()
2078    */
2079   public int getThresholdTextColour()
2080   {
2081     return viewStyle.getThresholdTextColour();
2082   }
2083
2084   /**
2085    * @return
2086    * @see jalview.api.ViewStyleI#isConservationColourSelected()
2087    */
2088   public boolean isConservationColourSelected()
2089   {
2090     return viewStyle.isConservationColourSelected();
2091   }
2092
2093   /**
2094    * @return
2095    * @see jalview.api.ViewStyleI#isRenderGaps()
2096    */
2097   public boolean isRenderGaps()
2098   {
2099     return viewStyle.isRenderGaps();
2100   }
2101
2102   /**
2103    * @return
2104    * @see jalview.api.ViewStyleI#isShowColourText()
2105    */
2106   public boolean isShowColourText()
2107   {
2108     return viewStyle.isShowColourText();
2109   }
2110   /**
2111    * @return
2112    * @see jalview.api.ViewStyleI#isShowSeqFeaturesHeight()
2113    */
2114   public boolean isShowSeqFeaturesHeight()
2115   {
2116     return viewStyle.isShowSeqFeaturesHeight();
2117   }
2118
2119   /**
2120    * @param conservationColourSelected
2121    * @see jalview.api.ViewStyleI#setConservationColourSelected(boolean)
2122    */
2123   public void setConservationColourSelected(
2124           boolean conservationColourSelected)
2125   {
2126     viewStyle.setConservationColourSelected(conservationColourSelected);
2127   }
2128
2129   /**
2130    * @param showColourText
2131    * @see jalview.api.ViewStyleI#setShowColourText(boolean)
2132    */
2133   public void setShowColourText(boolean showColourText)
2134   {
2135     viewStyle.setShowColourText(showColourText);
2136   }
2137
2138   /**
2139    * @param textColour
2140    * @see jalview.api.ViewStyleI#setTextColour(java.awt.Color)
2141    */
2142   public void setTextColour(Color textColour)
2143   {
2144     viewStyle.setTextColour(textColour);
2145   }
2146
2147   /**
2148    * @param thresholdTextColour
2149    * @see jalview.api.ViewStyleI#setThresholdTextColour(int)
2150    */
2151   public void setThresholdTextColour(int thresholdTextColour)
2152   {
2153     viewStyle.setThresholdTextColour(thresholdTextColour);
2154   }
2155
2156   /**
2157    * @param textColour2
2158    * @see jalview.api.ViewStyleI#setTextColour2(java.awt.Color)
2159    */
2160   public void setTextColour2(Color textColour2)
2161   {
2162     viewStyle.setTextColour2(textColour2);
2163   }
2164
2165   @Override
2166   public ViewStyleI getViewStyle()
2167   {
2168     return new ViewStyle(viewStyle);
2169   }
2170
2171   @Override
2172   public void setViewStyle(ViewStyleI settingsForView)
2173   {
2174     viewStyle = new ViewStyle(settingsForView);
2175   }
2176
2177   @Override
2178   public boolean sameStyle(ViewStyleI them)
2179   {
2180     return viewStyle.sameStyle(them);
2181   }
2182
2183   /**
2184    * @return
2185    * @see jalview.api.ViewStyleI#getIdWidth()
2186    */
2187   public int getIdWidth()
2188   {
2189     return viewStyle.getIdWidth();
2190   }
2191
2192   /**
2193    * @param i
2194    * @see jalview.api.ViewStyleI#setIdWidth(int)
2195    */
2196   public void setIdWidth(int i)
2197   {
2198     viewStyle.setIdWidth(i);
2199   }
2200
2201   /**
2202    * @return
2203    * @see jalview.api.ViewStyleI#isCentreColumnLabels()
2204    */
2205   public boolean isCentreColumnLabels()
2206   {
2207     return viewStyle.isCentreColumnLabels();
2208   }
2209
2210   /**
2211    * @param centreColumnLabels
2212    * @see jalview.api.ViewStyleI#setCentreColumnLabels(boolean)
2213    */
2214   public void setCentreColumnLabels(boolean centreColumnLabels)
2215   {
2216     viewStyle.setCentreColumnLabels(centreColumnLabels);
2217   }
2218
2219   /**
2220    * @param showdbrefs
2221    * @see jalview.api.ViewStyleI#setShowDBRefs(boolean)
2222    */
2223   public void setShowDBRefs(boolean showdbrefs)
2224   {
2225     viewStyle.setShowDBRefs(showdbrefs);
2226   }
2227
2228   /**
2229    * @return
2230    * @see jalview.api.ViewStyleI#isShowDBRefs()
2231    */
2232   public boolean isShowDBRefs()
2233   {
2234     return viewStyle.isShowDBRefs();
2235   }
2236
2237   /**
2238    * @return
2239    * @see jalview.api.ViewStyleI#isShowNPFeats()
2240    */
2241   public boolean isShowNPFeats()
2242   {
2243     return viewStyle.isShowNPFeats();
2244   }
2245
2246   /**
2247    * @param shownpfeats
2248    * @see jalview.api.ViewStyleI#setShowNPFeats(boolean)
2249    */
2250   public void setShowNPFeats(boolean shownpfeats)
2251   {
2252     viewStyle.setShowNPFeats(shownpfeats);
2253   }
2254
2255   public abstract StructureSelectionManager getStructureSelectionManager();
2256
2257   /**
2258    * Add one command to the command history list.
2259    * 
2260    * @param command
2261    */
2262   public void addToHistoryList(CommandI command)
2263   {
2264     if (this.historyList != null)
2265     {
2266       this.historyList.push(command);
2267       broadcastCommand(command, false);
2268     }
2269   }
2270
2271   protected void broadcastCommand(CommandI command, boolean undo)
2272   {
2273     getStructureSelectionManager().commandPerformed(command, undo, getVamsasSource());
2274   }
2275
2276   /**
2277    * Add one command to the command redo list.
2278    * 
2279    * @param command
2280    */
2281   public void addToRedoList(CommandI command)
2282   {
2283     if (this.redoList != null)
2284     {
2285       this.redoList.push(command);
2286     }
2287     broadcastCommand(command, true);
2288   }
2289
2290   /**
2291    * Clear the command redo list.
2292    */
2293   public void clearRedoList()
2294   {
2295     if (this.redoList != null)
2296     {
2297       this.redoList.clear();
2298     }
2299   }
2300
2301   public void setHistoryList(Deque<CommandI> list)
2302   {
2303     this.historyList = list;
2304   }
2305
2306   public Deque<CommandI> getHistoryList()
2307   {
2308     return this.historyList;
2309   }
2310
2311   public void setRedoList(Deque<CommandI> list)
2312   {
2313     this.redoList = list;
2314   }
2315
2316   public Deque<CommandI> getRedoList()
2317   {
2318     return this.redoList;
2319   }
2320
2321   @Override
2322   public VamsasSource getVamsasSource()
2323   {
2324     return this;
2325   }
2326 }