029280b2cb83657d8ec02f73598787d9d4652a19
[jalview.git] / src / jalview / viewmodel / AlignmentViewport.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.viewmodel;
19
20 import jalview.analysis.AAFrequency;
21 import jalview.analysis.Conservation;
22 import jalview.api.AlignCalcManagerI;
23 import jalview.api.AlignViewportI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.datamodel.AlignmentAnnotation;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.AlignmentView;
28 import jalview.datamodel.Annotation;
29 import jalview.datamodel.ColumnSelection;
30 import jalview.datamodel.Sequence;
31 import jalview.datamodel.SequenceCollectionI;
32 import jalview.datamodel.SequenceGroup;
33 import jalview.datamodel.SequenceI;
34 import jalview.schemes.Blosum62ColourScheme;
35 import jalview.schemes.ClustalxColourScheme;
36 import jalview.schemes.ColourSchemeI;
37 import jalview.schemes.PIDColourScheme;
38 import jalview.schemes.ResidueProperties;
39 import jalview.workers.AlignCalcManager;
40 import jalview.workers.ConsensusThread;
41 import jalview.workers.StrucConsensusThread;
42
43 import java.util.ArrayList;
44 import java.util.Hashtable;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Vector;
48
49 /**
50  * base class holding visualization and analysis attributes and common logic for
51  * an active alignment view displayed in the GUI
52  * 
53  * @author jimp
54  * 
55  */
56 public abstract class AlignmentViewport implements AlignViewportI
57 {
58   /**
59    * alignment displayed in the viewport. Please use get/setter
60    */
61   protected AlignmentI alignment;
62
63   protected String sequenceSetID;
64
65   /**
66    * probably unused indicator that view is of a dataset rather than an
67    * alignment
68    */
69   protected boolean isDataset = false;
70
71   private Map<SequenceI, SequenceCollectionI> hiddenRepSequences;
72
73   protected ColumnSelection colSel = new ColumnSelection();
74
75   public boolean autoCalculateConsensus = true;
76
77   protected boolean autoCalculateStrucConsensus = true;
78
79   protected boolean ignoreGapsInConsensusCalculation = false;
80
81   protected ColourSchemeI globalColourScheme = null;
82
83   /**
84    * gui state - changes to colour scheme propagated to all groups
85    */
86   private boolean colourAppliesToAllGroups;
87
88   /**
89    * @param value
90    *          indicating if subsequent colourscheme changes will be propagated
91    *          to all groups
92    */
93   public void setColourAppliesToAllGroups(boolean b)
94   {
95     colourAppliesToAllGroups = b;
96   }
97
98   /**
99    * 
100    * 
101    * @return flag indicating if colourchanges propagated to all groups
102    */
103   public boolean getColourAppliesToAllGroups()
104   {
105     return colourAppliesToAllGroups;
106   }
107
108   boolean abovePIDThreshold = false;
109
110   /**
111    * GUI state
112    * 
113    * @return true if percent identity threshold is applied to shading
114    */
115   public boolean getAbovePIDThreshold()
116   {
117     return abovePIDThreshold;
118   }
119
120   /**
121    * GUI state
122    * 
123    * 
124    * @param b
125    *          indicate if percent identity threshold is applied to shading
126    */
127   public void setAbovePIDThreshold(boolean b)
128   {
129     abovePIDThreshold = b;
130   }
131
132   int threshold;
133
134   /**
135    * DOCUMENT ME!
136    * 
137    * @param thresh
138    *          DOCUMENT ME!
139    */
140   public void setThreshold(int thresh)
141   {
142     threshold = thresh;
143   }
144
145   /**
146    * DOCUMENT ME!
147    * 
148    * @return DOCUMENT ME!
149    */
150   public int getThreshold()
151   {
152     return threshold;
153   }
154
155   int increment;
156
157   /**
158    * 
159    * @param inc
160    *          set the scalar for bleaching colourschemes according to degree of
161    *          conservation
162    */
163   public void setIncrement(int inc)
164   {
165     increment = inc;
166   }
167
168   /**
169    * GUI State
170    * 
171    * @return get scalar for bleaching colourschemes by conservation
172    */
173   public int getIncrement()
174   {
175     return increment;
176   }
177
178   boolean conservationColourSelected = false;
179
180   /**
181    * GUI state
182    * 
183    * @return true if conservation based shading is enabled
184    */
185   public boolean getConservationSelected()
186   {
187     return conservationColourSelected;
188   }
189
190   /**
191    * GUI state
192    * 
193    * @param b
194    *          enable conservation based shading
195    */
196   public void setConservationSelected(boolean b)
197   {
198     conservationColourSelected = b;
199   }
200
201   @Override
202   public void setGlobalColourScheme(ColourSchemeI cs)
203   {
204     // TODO: logic refactored from AlignFrame changeColour -
205     // autorecalc stuff should be changed to rely on the worker system
206     // check to see if we should implement a changeColour(cs) method rather than
207     // put th logic in here
208     // - means that caller decides if they want to just modify state and defer
209     // calculation till later or to do all calculations in thread.
210     // via changecolour
211     globalColourScheme = cs;
212     if (getColourAppliesToAllGroups())
213     {
214       for (SequenceGroup sg : getAlignment().getGroups())
215       {
216         if (cs == null)
217         {
218           sg.cs = null;
219           continue;
220         }
221         if (cs instanceof ClustalxColourScheme)
222         {
223           sg.cs = new ClustalxColourScheme(sg, getHiddenRepSequences());
224         }
225         else
226         {
227           try
228           {
229             sg.cs = cs.getClass().newInstance();
230           } catch (Exception ex)
231           {
232             ex.printStackTrace();
233             sg.cs = cs;
234           }
235         }
236
237         if (getAbovePIDThreshold() || cs instanceof PIDColourScheme
238                 || cs instanceof Blosum62ColourScheme)
239         {
240           sg.cs.setThreshold(threshold, getIgnoreGapsConsensus());
241           sg.cs.setConsensus(AAFrequency.calculate(
242                   sg.getSequences(getHiddenRepSequences()), 0,
243                   sg.getWidth()));
244         }
245         else
246         {
247           sg.cs.setThreshold(0, getIgnoreGapsConsensus());
248         }
249
250         if (getConservationSelected())
251         {
252           Conservation c = new Conservation("Group",
253                   ResidueProperties.propHash, 3,
254                   sg.getSequences(getHiddenRepSequences()), 0,
255                   getAlignment().getWidth() - 1);
256           c.calculate();
257           c.verdict(false, getConsPercGaps());
258           sg.cs.setConservation(c);
259         }
260         else
261         {
262           sg.cs.setConservation(null);
263           sg.cs.setThreshold(0, getIgnoreGapsConsensus());
264         }
265
266       }
267     }
268
269   }
270
271   @Override
272   public ColourSchemeI getGlobalColourScheme()
273   {
274     return globalColourScheme;
275   }
276
277   protected AlignmentAnnotation consensus;
278
279   protected AlignmentAnnotation strucConsensus;
280
281   protected AlignmentAnnotation conservation;
282
283   protected AlignmentAnnotation quality;
284
285   protected AlignmentAnnotation[] groupConsensus;
286
287   protected AlignmentAnnotation[] groupConservation;
288
289   /**
290    * results of alignment consensus analysis for visible portion of view
291    */
292   protected Hashtable[] hconsensus = null;
293
294   /**
295    * results of secondary structure base pair consensus for visible portion of
296    * view
297    */
298   protected Hashtable[] hStrucConsensus = null;
299
300   /**
301    * percentage gaps allowed in a column before all amino acid properties should
302    * be considered unconserved
303    */
304   int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
305
306   @Override
307   public int getConsPercGaps()
308   {
309     return ConsPercGaps;
310   }
311
312   @Override
313   public void setSequenceConsensusHash(Hashtable[] hconsensus)
314   {
315     this.hconsensus = hconsensus;
316
317   }
318
319   @Override
320   public Hashtable[] getSequenceConsensusHash()
321   {
322     return hconsensus;
323   }
324
325   @Override
326   public Hashtable[] getRnaStructureConsensusHash()
327   {
328     return hStrucConsensus;
329   }
330
331   @Override
332   public void setRnaStructureConsensusHash(Hashtable[] hStrucConsensus)
333   {
334     this.hStrucConsensus = hStrucConsensus;
335
336   }
337
338   @Override
339   public AlignmentAnnotation getAlignmentQualityAnnot()
340   {
341     return quality;
342   }
343
344   @Override
345   public AlignmentAnnotation getAlignmentConservationAnnotation()
346   {
347     return conservation;
348   }
349
350   @Override
351   public AlignmentAnnotation getAlignmentConsensusAnnotation()
352   {
353     return consensus;
354   }
355
356   @Override
357   public AlignmentAnnotation getAlignmentStrucConsensusAnnotation()
358   {
359     return strucConsensus;
360   }
361
362   protected AlignCalcManagerI calculator = new AlignCalcManager();
363
364   /**
365    * trigger update of conservation annotation
366    */
367   public void updateConservation(final AlignmentViewPanel ap)
368   {
369     // see note in mantis : issue number 8585
370     if (alignment.isNucleotide() || conservation == null
371             || !autoCalculateConsensus)
372     {
373       return;
374     }
375     if (calculator
376             .getRegisteredWorkersOfClass(jalview.workers.ConservationThread.class) == null)
377     {
378       calculator.registerWorker(new jalview.workers.ConservationThread(
379               this, ap));
380     }
381   }
382
383   /**
384    * trigger update of consensus annotation
385    */
386   public void updateConsensus(final AlignmentViewPanel ap)
387   {
388     // see note in mantis : issue number 8585
389     if (consensus == null || !autoCalculateConsensus)
390     {
391       return;
392     }
393     if (calculator.getRegisteredWorkersOfClass(ConsensusThread.class) == null)
394     {
395       calculator.registerWorker(new ConsensusThread(this, ap));
396     }
397   }
398
399   // --------START Structure Conservation
400   public void updateStrucConsensus(final AlignmentViewPanel ap)
401   {
402     if (autoCalculateStrucConsensus && strucConsensus == null
403             && alignment.isNucleotide() && alignment.hasRNAStructure())
404     {
405
406     }
407
408     // see note in mantis : issue number 8585
409     if (strucConsensus == null || !autoCalculateStrucConsensus)
410     {
411       return;
412     }
413     if (calculator.getRegisteredWorkersOfClass(StrucConsensusThread.class) == null)
414     {
415       calculator.registerWorker(new StrucConsensusThread(this, ap));
416     }
417   }
418
419   public boolean isCalcInProgress()
420   {
421     return calculator.isWorking();
422   }
423
424   @Override
425   public boolean isCalculationInProgress(
426           AlignmentAnnotation alignmentAnnotation)
427   {
428     if (!alignmentAnnotation.autoCalculated)
429       return false;
430     if (calculator.workingInvolvedWith(alignmentAnnotation))
431     {
432       // System.err.println("grey out ("+alignmentAnnotation.label+")");
433       return true;
434     }
435     return false;
436   }
437
438   @Override
439   public boolean isClosed()
440   {
441     // TODO: check that this isClosed is only true after panel is closed, not
442     // before it is fully constructed.
443     return alignment == null;
444   }
445
446   @Override
447   public AlignCalcManagerI getCalcManager()
448   {
449     return calculator;
450   }
451
452   /**
453    * should conservation rows be shown for groups
454    */
455   protected boolean showGroupConservation = false;
456
457   /**
458    * should consensus rows be shown for groups
459    */
460   protected boolean showGroupConsensus = false;
461
462   /**
463    * should consensus profile be rendered by default
464    */
465   protected boolean showSequenceLogo = false;
466
467   /**
468    * should consensus profile be rendered normalised to row height
469    */
470   protected boolean normaliseSequenceLogo = false;
471
472   /**
473    * should consensus histograms be rendered by default
474    */
475   protected boolean showConsensusHistogram = true;
476
477   /**
478    * @return the showConsensusProfile
479    */
480   @Override
481   public boolean isShowSequenceLogo()
482   {
483     return showSequenceLogo;
484   }
485
486   /**
487    * @param showSequenceLogo
488    *          the new value
489    */
490   public void setShowSequenceLogo(boolean showSequenceLogo)
491   {
492     if (showSequenceLogo != this.showSequenceLogo)
493     {
494       // TODO: decouple settings setting from calculation when refactoring
495       // annotation update method from alignframe to viewport
496       this.showSequenceLogo = showSequenceLogo;
497       calculator.updateAnnotationFor(ConsensusThread.class);
498       calculator.updateAnnotationFor(StrucConsensusThread.class);
499     }
500     this.showSequenceLogo = showSequenceLogo;
501   }
502
503   /**
504    * @param showConsensusHistogram
505    *          the showConsensusHistogram to set
506    */
507   public void setShowConsensusHistogram(boolean showConsensusHistogram)
508   {
509     this.showConsensusHistogram = showConsensusHistogram;
510   }
511
512   /**
513    * @return the showGroupConservation
514    */
515   public boolean isShowGroupConservation()
516   {
517     return showGroupConservation;
518   }
519
520   /**
521    * @param showGroupConservation
522    *          the showGroupConservation to set
523    */
524   public void setShowGroupConservation(boolean showGroupConservation)
525   {
526     this.showGroupConservation = showGroupConservation;
527   }
528
529   /**
530    * @return the showGroupConsensus
531    */
532   public boolean isShowGroupConsensus()
533   {
534     return showGroupConsensus;
535   }
536
537   /**
538    * @param showGroupConsensus
539    *          the showGroupConsensus to set
540    */
541   public void setShowGroupConsensus(boolean showGroupConsensus)
542   {
543     this.showGroupConsensus = showGroupConsensus;
544   }
545
546   /**
547    * 
548    * @return flag to indicate if the consensus histogram should be rendered by
549    *         default
550    */
551   @Override
552   public boolean isShowConsensusHistogram()
553   {
554     return this.showConsensusHistogram;
555   }
556
557   /**
558    * show non-conserved residues only
559    */
560   protected boolean showUnconserved = false;
561
562   /**
563    * when set, updateAlignment will always ensure sequences are of equal length
564    */
565   private boolean padGaps = false;
566
567   /**
568    * when set, alignment should be reordered according to a newly opened tree
569    */
570   public boolean sortByTree = false;
571
572   public boolean getShowUnconserved()
573   {
574     return showUnconserved;
575   }
576
577   public void setShowUnconserved(boolean showunconserved)
578   {
579     showUnconserved = showunconserved;
580   }
581
582   /**
583    * @param showNonconserved
584    *          the showUnconserved to set
585    */
586   public void setShowunconserved(boolean displayNonconserved)
587   {
588     this.showUnconserved = displayNonconserved;
589   }
590
591   /**
592    * 
593    * 
594    * @return null or the currently selected sequence region
595    */
596   public SequenceGroup getSelectionGroup()
597   {
598     return selectionGroup;
599   }
600
601   /**
602    * Set the selection group for this window.
603    * 
604    * @param sg
605    *          - group holding references to sequences in this alignment view
606    * 
607    */
608   public void setSelectionGroup(SequenceGroup sg)
609   {
610     selectionGroup = sg;
611   }
612
613   public void setHiddenColumns(ColumnSelection colsel)
614   {
615     this.colSel = colsel;
616     if (colSel.getHiddenColumns() != null)
617     {
618       hasHiddenColumns = true;
619     }
620   }
621
622   @Override
623   public ColumnSelection getColumnSelection()
624   {
625     return colSel;
626   }
627
628   public void setColumnSelection(ColumnSelection colSel)
629   {
630     this.colSel = colSel;
631   }
632
633   /**
634    * 
635    * @return
636    */
637   @Override
638   public Map<SequenceI, SequenceCollectionI> getHiddenRepSequences()
639   {
640     return hiddenRepSequences;
641   }
642
643   @Override
644   public void setHiddenRepSequences(
645           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
646   {
647     this.hiddenRepSequences = hiddenRepSequences;
648   }
649
650   protected boolean hasHiddenColumns = false;
651
652   public void updateHiddenColumns()
653   {
654     hasHiddenColumns = colSel.getHiddenColumns() != null;
655   }
656
657   protected boolean hasHiddenRows = false;
658
659   public boolean hasHiddenRows()
660   {
661     return hasHiddenRows;
662   }
663
664   protected SequenceGroup selectionGroup;
665
666   public void setSequenceSetId(String newid)
667   {
668     if (sequenceSetID != null)
669     {
670       System.err
671               .println("Warning - overwriting a sequenceSetId for a viewport!");
672     }
673     sequenceSetID = new String(newid);
674   }
675
676   public String getSequenceSetId()
677   {
678     if (sequenceSetID == null)
679     {
680       sequenceSetID = alignment.hashCode() + "";
681     }
682
683     return sequenceSetID;
684   }
685
686   /**
687    * unique viewId for synchronizing state (e.g. with stored Jalview Project)
688    * 
689    */
690   protected String viewId = null;
691
692   public String getViewId()
693   {
694     if (viewId == null)
695     {
696       viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
697     }
698     return viewId;
699   }
700
701   public void setIgnoreGapsConsensus(boolean b, AlignmentViewPanel ap)
702   {
703     ignoreGapsInConsensusCalculation = b;
704     if (ap != null)
705     {
706       updateConsensus(ap);
707       if (globalColourScheme != null)
708       {
709         globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
710                 ignoreGapsInConsensusCalculation);
711       }
712     }
713
714   }
715
716   private long sgrouphash = -1, colselhash = -1;
717
718   /**
719    * checks current SelectionGroup against record of last hash value, and
720    * updates record.
721    * 
722    * @param b
723    *          update the record of last hash value
724    * 
725    * @return true if SelectionGroup changed since last call (when b is true)
726    */
727   public boolean isSelectionGroupChanged(boolean b)
728   {
729     int hc = (selectionGroup == null || selectionGroup.getSize() == 0) ? -1
730             : selectionGroup.hashCode();
731     if (hc != -1 && hc != sgrouphash)
732     {
733       if (b)
734       {
735         sgrouphash = hc;
736       }
737       return true;
738     }
739     return false;
740   }
741
742   /**
743    * checks current colsel against record of last hash value, and optionally
744    * updates record.
745    * 
746    * @param b
747    *          update the record of last hash value
748    * @return true if colsel changed since last call (when b is true)
749    */
750   public boolean isColSelChanged(boolean b)
751   {
752     int hc = (colSel == null || colSel.size() == 0) ? -1 : colSel
753             .hashCode();
754     if (hc != -1 && hc != colselhash)
755     {
756       if (b)
757       {
758         colselhash = hc;
759       }
760       return true;
761     }
762     return false;
763   }
764
765   @Override
766   public boolean getIgnoreGapsConsensus()
767   {
768     return ignoreGapsInConsensusCalculation;
769   }
770
771   // / property change stuff
772
773   // JBPNote Prolly only need this in the applet version.
774   private final java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
775           this);
776
777   protected boolean showConservation = true;
778
779   protected boolean showQuality = true;
780
781   protected boolean showConsensus = true;
782
783   /**
784    * Property change listener for changes in alignment
785    * 
786    * @param listener
787    *          DOCUMENT ME!
788    */
789   public void addPropertyChangeListener(
790           java.beans.PropertyChangeListener listener)
791   {
792     changeSupport.addPropertyChangeListener(listener);
793   }
794
795   /**
796    * DOCUMENT ME!
797    * 
798    * @param listener
799    *          DOCUMENT ME!
800    */
801   public void removePropertyChangeListener(
802           java.beans.PropertyChangeListener listener)
803   {
804     changeSupport.removePropertyChangeListener(listener);
805   }
806
807   /**
808    * Property change listener for changes in alignment
809    * 
810    * @param prop
811    *          DOCUMENT ME!
812    * @param oldvalue
813    *          DOCUMENT ME!
814    * @param newvalue
815    *          DOCUMENT ME!
816    */
817   public void firePropertyChange(String prop, Object oldvalue,
818           Object newvalue)
819   {
820     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
821   }
822
823   // common hide/show column stuff
824
825   public void hideSelectedColumns()
826   {
827     if (colSel.size() < 1)
828     {
829       return;
830     }
831
832     colSel.hideSelectedColumns();
833     setSelectionGroup(null);
834
835     hasHiddenColumns = true;
836   }
837
838   public void hideColumns(int start, int end)
839   {
840     if (start == end)
841     {
842       colSel.hideColumns(start);
843     }
844     else
845     {
846       colSel.hideColumns(start, end);
847     }
848
849     hasHiddenColumns = true;
850   }
851
852   public void showColumn(int col)
853   {
854     colSel.revealHiddenColumns(col);
855     if (colSel.getHiddenColumns() == null)
856     {
857       hasHiddenColumns = false;
858     }
859   }
860
861   public void showAllHiddenColumns()
862   {
863     colSel.revealAllHiddenColumns();
864     hasHiddenColumns = false;
865   }
866
867   // common hide/show seq stuff
868   public void showAllHiddenSeqs()
869   {
870     if (alignment.getHiddenSequences().getSize() > 0)
871     {
872       if (selectionGroup == null)
873       {
874         selectionGroup = new SequenceGroup();
875         selectionGroup.setEndRes(alignment.getWidth() - 1);
876       }
877       Vector tmp = alignment.getHiddenSequences().showAll(
878               hiddenRepSequences);
879       for (int t = 0; t < tmp.size(); t++)
880       {
881         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
882       }
883
884       hasHiddenRows = false;
885       hiddenRepSequences = null;
886
887       firePropertyChange("alignment", null, alignment.getSequences());
888       // used to set hasHiddenRows/hiddenRepSequences here, after the property
889       // changed event
890       sendSelection();
891     }
892   }
893
894   public void showSequence(int index)
895   {
896     Vector tmp = alignment.getHiddenSequences().showSequence(index,
897             hiddenRepSequences);
898     if (tmp.size() > 0)
899     {
900       if (selectionGroup == null)
901       {
902         selectionGroup = new SequenceGroup();
903         selectionGroup.setEndRes(alignment.getWidth() - 1);
904       }
905
906       for (int t = 0; t < tmp.size(); t++)
907       {
908         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
909       }
910       // JBPNote: refactor: only update flag if we modified visiblity (used to
911       // do this regardless)
912       if (alignment.getHiddenSequences().getSize() < 1)
913       {
914         hasHiddenRows = false;
915       }
916       firePropertyChange("alignment", null, alignment.getSequences());
917       sendSelection();
918     }
919   }
920
921   public void hideAllSelectedSeqs()
922   {
923     if (selectionGroup == null || selectionGroup.getSize() < 1)
924     {
925       return;
926     }
927
928     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
929
930     hideSequence(seqs);
931
932     setSelectionGroup(null);
933   }
934
935   public void hideSequence(SequenceI[] seq)
936   {
937     if (seq != null)
938     {
939       for (int i = 0; i < seq.length; i++)
940       {
941         alignment.getHiddenSequences().hideSequence(seq[i]);
942       }
943       hasHiddenRows = true;
944       firePropertyChange("alignment", null, alignment.getSequences());
945     }
946   }
947
948   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
949   {
950     int sSize = sg.getSize();
951     if (sSize < 2)
952     {
953       return;
954     }
955
956     if (hiddenRepSequences == null)
957     {
958       hiddenRepSequences = new Hashtable();
959     }
960
961     hiddenRepSequences.put(repSequence, sg);
962
963     // Hide all sequences except the repSequence
964     SequenceI[] seqs = new SequenceI[sSize - 1];
965     int index = 0;
966     for (int i = 0; i < sSize; i++)
967     {
968       if (sg.getSequenceAt(i) != repSequence)
969       {
970         if (index == sSize - 1)
971         {
972           return;
973         }
974
975         seqs[index++] = sg.getSequenceAt(i);
976       }
977     }
978     sg.setSeqrep(repSequence); // note: not done in 2.7applet
979     sg.setHidereps(true); // note: not done in 2.7applet
980     hideSequence(seqs);
981
982   }
983
984   public boolean isHiddenRepSequence(SequenceI seq)
985   {
986     return hiddenRepSequences != null
987             && hiddenRepSequences.containsKey(seq);
988   }
989
990   public SequenceGroup getRepresentedSequences(SequenceI seq)
991   {
992     return (SequenceGroup) (hiddenRepSequences == null ? null
993             : hiddenRepSequences.get(seq));
994   }
995
996   public int adjustForHiddenSeqs(int alignmentIndex)
997   {
998     return alignment.getHiddenSequences().adjustForHiddenSeqs(
999             alignmentIndex);
1000   }
1001
1002   // Selection manipulation
1003   /**
1004    * broadcast selection to any interested parties
1005    */
1006   public abstract void sendSelection();
1007
1008   public void invertColumnSelection()
1009   {
1010     colSel.invertColumnSelection(0, alignment.getWidth());
1011   }
1012
1013   /**
1014    * This method returns an array of new SequenceI objects derived from the
1015    * whole alignment or just the current selection with start and end points
1016    * adjusted
1017    * 
1018    * @note if you need references to the actual SequenceI objects in the
1019    *       alignment or currently selected then use getSequenceSelection()
1020    * @return selection as new sequenceI objects
1021    */
1022   public SequenceI[] getSelectionAsNewSequence()
1023   {
1024     SequenceI[] sequences;
1025     // JBPNote: Need to test jalviewLite.getSelectedSequencesAsAlignmentFrom -
1026     // this was the only caller in the applet for this method
1027     // JBPNote: in applet, this method returned references to the alignment
1028     // sequences, and it did not honour the presence/absence of annotation
1029     // attached to the alignment (probably!)
1030     if (selectionGroup == null || selectionGroup.getSize() == 0)
1031     {
1032       sequences = alignment.getSequencesArray();
1033       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
1034       for (int i = 0; i < sequences.length; i++)
1035       {
1036         sequences[i] = new Sequence(sequences[i], annots); // construct new
1037         // sequence with
1038         // subset of visible
1039         // annotation
1040       }
1041     }
1042     else
1043     {
1044       sequences = selectionGroup.getSelectionAsNewSequences(alignment);
1045     }
1046
1047     return sequences;
1048   }
1049
1050   /**
1051    * get the currently selected sequence objects or all the sequences in the
1052    * alignment.
1053    * 
1054    * @return array of references to sequence objects
1055    */
1056   public SequenceI[] getSequenceSelection()
1057   {
1058     SequenceI[] sequences = null;
1059     if (selectionGroup != null)
1060     {
1061       sequences = selectionGroup.getSequencesInOrder(alignment);
1062     }
1063     if (sequences == null)
1064     {
1065       sequences = alignment.getSequencesArray();
1066     }
1067     return sequences;
1068   }
1069
1070   /**
1071    * This method returns the visible alignment as text, as seen on the GUI, ie
1072    * if columns are hidden they will not be returned in the result. Use this for
1073    * calculating trees, PCA, redundancy etc on views which contain hidden
1074    * columns.
1075    * 
1076    * @return String[]
1077    */
1078   public jalview.datamodel.CigarArray getViewAsCigars(
1079           boolean selectedRegionOnly)
1080   {
1081     return new jalview.datamodel.CigarArray(alignment,
1082             (hasHiddenColumns ? colSel : null),
1083             (selectedRegionOnly ? selectionGroup : null));
1084   }
1085
1086   /**
1087    * return a compact representation of the current alignment selection to pass
1088    * to an analysis function
1089    * 
1090    * @param selectedOnly
1091    *          boolean true to just return the selected view
1092    * @return AlignmentView
1093    */
1094   public jalview.datamodel.AlignmentView getAlignmentView(
1095           boolean selectedOnly)
1096   {
1097     return getAlignmentView(selectedOnly, false);
1098   }
1099
1100   /**
1101    * return a compact representation of the current alignment selection to pass
1102    * to an analysis function
1103    * 
1104    * @param selectedOnly
1105    *          boolean true to just return the selected view
1106    * @param markGroups
1107    *          boolean true to annotate the alignment view with groups on the
1108    *          alignment (and intersecting with selected region if selectedOnly
1109    *          is true)
1110    * @return AlignmentView
1111    */
1112   public jalview.datamodel.AlignmentView getAlignmentView(
1113           boolean selectedOnly, boolean markGroups)
1114   {
1115     return new AlignmentView(alignment, colSel, selectionGroup,
1116             hasHiddenColumns, selectedOnly, markGroups);
1117   }
1118
1119   /**
1120    * This method returns the visible alignment as text, as seen on the GUI, ie
1121    * if columns are hidden they will not be returned in the result. Use this for
1122    * calculating trees, PCA, redundancy etc on views which contain hidden
1123    * columns.
1124    * 
1125    * @return String[]
1126    */
1127   public String[] getViewAsString(boolean selectedRegionOnly)
1128   {
1129     String[] selection = null;
1130     SequenceI[] seqs = null;
1131     int i, iSize;
1132     int start = 0, end = 0;
1133     if (selectedRegionOnly && selectionGroup != null)
1134     {
1135       iSize = selectionGroup.getSize();
1136       seqs = selectionGroup.getSequencesInOrder(alignment);
1137       start = selectionGroup.getStartRes();
1138       end = selectionGroup.getEndRes() + 1;
1139     }
1140     else
1141     {
1142       iSize = alignment.getHeight();
1143       seqs = alignment.getSequencesArray();
1144       end = alignment.getWidth();
1145     }
1146
1147     selection = new String[iSize];
1148     if (hasHiddenColumns)
1149     {
1150       selection = colSel.getVisibleSequenceStrings(start, end, seqs);
1151     }
1152     else
1153     {
1154       for (i = 0; i < iSize; i++)
1155       {
1156         selection[i] = seqs[i].getSequenceAsString(start, end);
1157       }
1158
1159     }
1160     return selection;
1161   }
1162
1163   /**
1164    * return visible region boundaries within given column range
1165    * 
1166    * @param min
1167    *          first column (inclusive, from 0)
1168    * @param max
1169    *          last column (exclusive)
1170    * @return int[][] range of {start,end} visible positions
1171    */
1172   public int[][] getVisibleRegionBoundaries(int min, int max)
1173   {
1174     Vector regions = new Vector();
1175     int start = min;
1176     int end = max;
1177
1178     do
1179     {
1180       if (hasHiddenColumns)
1181       {
1182         if (start == 0)
1183         {
1184           start = colSel.adjustForHiddenColumns(start);
1185         }
1186
1187         end = colSel.getHiddenBoundaryRight(start);
1188         if (start == end)
1189         {
1190           end = max;
1191         }
1192         if (end > max)
1193         {
1194           end = max;
1195         }
1196       }
1197
1198       regions.addElement(new int[]
1199       { start, end });
1200
1201       if (hasHiddenColumns)
1202       {
1203         start = colSel.adjustForHiddenColumns(end);
1204         start = colSel.getHiddenBoundaryLeft(start) + 1;
1205       }
1206     } while (end < max);
1207
1208     int[][] startEnd = new int[regions.size()][2];
1209
1210     regions.copyInto(startEnd);
1211
1212     return startEnd;
1213
1214   }
1215
1216   /**
1217    * @return the padGaps
1218    */
1219   public boolean isPadGaps()
1220   {
1221     return padGaps;
1222   }
1223
1224   /**
1225    * @param padGaps
1226    *          the padGaps to set
1227    */
1228   public void setPadGaps(boolean padGaps)
1229   {
1230     this.padGaps = padGaps;
1231   }
1232
1233   /**
1234    * apply any post-edit constraints and trigger any calculations needed after
1235    * an edit has been performed on the alignment
1236    * 
1237    * @param ap
1238    */
1239   public void alignmentChanged(AlignmentViewPanel ap)
1240   {
1241     if (isPadGaps())
1242     {
1243       alignment.padGaps();
1244     }
1245     if (autoCalculateConsensus)
1246     {
1247       updateConsensus(ap);
1248     }
1249     if (hconsensus != null && autoCalculateConsensus)
1250     {
1251       updateConservation(ap);
1252     }
1253     if (autoCalculateStrucConsensus)
1254     {
1255       updateStrucConsensus(ap);
1256     }
1257
1258     // Reset endRes of groups if beyond alignment width
1259     int alWidth = alignment.getWidth();
1260     List<SequenceGroup> groups = alignment.getGroups();
1261     if (groups != null)
1262     {
1263       for (SequenceGroup sg : groups)
1264       {
1265         if (sg.getEndRes() > alWidth)
1266         {
1267           sg.setEndRes(alWidth - 1);
1268         }
1269       }
1270     }
1271
1272     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1273     {
1274       selectionGroup.setEndRes(alWidth - 1);
1275     }
1276
1277     resetAllColourSchemes();
1278     calculator.restartWorkers();
1279     // alignment.adjustSequenceAnnotations();
1280   }
1281
1282   /**
1283    * reset scope and do calculations for all applied colourschemes on alignment
1284    */
1285   void resetAllColourSchemes()
1286   {
1287     ColourSchemeI cs = globalColourScheme;
1288     if (cs != null)
1289     {
1290       cs.alignmentChanged(alignment, null);
1291
1292       cs.setConsensus(hconsensus);
1293       if (cs.conservationApplied())
1294       {
1295         cs.setConservation(Conservation.calculateConservation("All",
1296                 ResidueProperties.propHash, 3, alignment.getSequences(), 0,
1297                 alignment.getWidth(), false, getConsPercGaps(), false));
1298       }
1299     }
1300
1301     for (SequenceGroup sg : alignment.getGroups())
1302     {
1303       if (sg.cs != null)
1304       {
1305         sg.cs.alignmentChanged(sg, hiddenRepSequences);
1306       }
1307       sg.recalcConservation();
1308     }
1309   }
1310
1311   protected void initAutoAnnotation()
1312   {
1313     // TODO: add menu option action that nulls or creates consensus object
1314     // depending on if the user wants to see the annotation or not in a
1315     // specific alignment
1316
1317     if (hconsensus == null && !isDataset)
1318     {
1319       if (!alignment.isNucleotide())
1320       {
1321         if (showConservation)
1322         {
1323           if (conservation == null)
1324           {
1325             conservation = new AlignmentAnnotation("Conservation",
1326                     "Conservation of total alignment less than "
1327                             + getConsPercGaps() + "% gaps",
1328                     new Annotation[1], 0f, 11f,
1329                     AlignmentAnnotation.BAR_GRAPH);
1330             conservation.hasText = true;
1331             conservation.autoCalculated = true;
1332             alignment.addAnnotation(conservation);
1333           }
1334         }
1335         if (showQuality)
1336         {
1337           if (quality == null)
1338           {
1339             quality = new AlignmentAnnotation("Quality",
1340                     "Alignment Quality based on Blosum62 scores",
1341                     new Annotation[1], 0f, 11f,
1342                     AlignmentAnnotation.BAR_GRAPH);
1343             quality.hasText = true;
1344             quality.autoCalculated = true;
1345             alignment.addAnnotation(quality);
1346           }
1347         }
1348       }
1349       else
1350       {
1351         if (alignment.hasRNAStructure())
1352         {
1353           strucConsensus = new AlignmentAnnotation("StrucConsensus", "PID",
1354                   new Annotation[1], 0f, 100f,
1355                   AlignmentAnnotation.BAR_GRAPH);
1356           strucConsensus.hasText = true;
1357           strucConsensus.autoCalculated = true;
1358         }
1359       }
1360
1361       consensus = new AlignmentAnnotation("Consensus", "PID",
1362               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
1363       consensus.hasText = true;
1364       consensus.autoCalculated = true;
1365
1366       if (showConsensus)
1367       {
1368         alignment.addAnnotation(consensus);
1369         if (strucConsensus != null)
1370         {
1371           alignment.addAnnotation(strucConsensus);
1372         }
1373       }
1374     }
1375   }
1376
1377   /*
1378    * (non-Javadoc)
1379    * 
1380    * @see jalview.api.AlignViewportI#calcPanelHeight()
1381    */
1382   public int calcPanelHeight()
1383   {
1384     // setHeight of panels
1385     AlignmentAnnotation[] aa = getAlignment().getAlignmentAnnotation();
1386     int height = 0;
1387     int charHeight = getCharHeight();
1388     if (aa != null)
1389     {
1390       boolean graphgrp[] = null;
1391       for (int i = 0; i < aa.length; i++)
1392       {
1393         if (aa[i] == null)
1394         {
1395           System.err.println("Null annotation row: ignoring.");
1396           continue;
1397         }
1398         if (!aa[i].visible)
1399         {
1400           continue;
1401         }
1402         if (aa[i].graphGroup > -1)
1403         {
1404           if (graphgrp == null)
1405           {
1406             graphgrp = new boolean[aa.length];
1407           }
1408           if (graphgrp[aa[i].graphGroup])
1409           {
1410             continue;
1411           }
1412           else
1413           {
1414             graphgrp[aa[i].graphGroup] = true;
1415           }
1416         }
1417         aa[i].height = 0;
1418
1419         if (aa[i].hasText)
1420         {
1421           aa[i].height += charHeight;
1422         }
1423
1424         if (aa[i].hasIcons)
1425         {
1426           aa[i].height += 16;
1427         }
1428
1429         if (aa[i].graph > 0)
1430         {
1431           aa[i].height += aa[i].graphHeight;
1432         }
1433
1434         if (aa[i].height == 0)
1435         {
1436           aa[i].height = 20;
1437         }
1438
1439         height += aa[i].height;
1440       }
1441     }
1442     if (height == 0)
1443     {
1444       // set minimum
1445       height = 20;
1446     }
1447     return height;
1448   }
1449
1450   @Override
1451   public void updateGroupAnnotationSettings(boolean applyGlobalSettings,
1452           boolean preserveNewGroupSettings)
1453   {
1454     boolean updateCalcs = false;
1455     boolean conv = isShowGroupConservation();
1456     boolean cons = isShowGroupConsensus();
1457     boolean showprf = isShowSequenceLogo();
1458     boolean showConsHist = isShowConsensusHistogram();
1459     boolean normLogo = isNormaliseSequenceLogo();
1460
1461     /**
1462      * TODO reorder the annotation rows according to group/sequence ordering on
1463      * alignment
1464      */
1465     boolean sortg = true;
1466
1467     // remove old automatic annotation
1468     // add any new annotation
1469
1470     // intersect alignment annotation with alignment groups
1471
1472     AlignmentAnnotation[] aan = alignment.getAlignmentAnnotation();
1473     List<SequenceGroup> oldrfs = new ArrayList<SequenceGroup>();
1474     if (aan != null)
1475     {
1476       for (int an = 0; an < aan.length; an++)
1477       {
1478         if (aan[an].autoCalculated && aan[an].groupRef != null)
1479         {
1480           oldrfs.add(aan[an].groupRef);
1481           alignment.deleteAnnotation(aan[an]);
1482           aan[an] = null;
1483         }
1484       }
1485     }
1486     if (alignment.getGroups() != null)
1487     {
1488       for (SequenceGroup sg : alignment.getGroups())
1489       {
1490         updateCalcs = false;
1491         if (applyGlobalSettings
1492                 || (!preserveNewGroupSettings && !oldrfs.contains(sg)))
1493         {
1494           // set defaults for this group's conservation/consensus
1495           sg.setshowSequenceLogo(showprf);
1496           sg.setShowConsensusHistogram(showConsHist);
1497           sg.setNormaliseSequenceLogo(normLogo);
1498         }
1499         if (conv)
1500         {
1501           updateCalcs = true;
1502           alignment.addAnnotation(sg.getConservationRow(), 0);
1503         }
1504         if (cons)
1505         {
1506           updateCalcs = true;
1507           alignment.addAnnotation(sg.getConsensus(), 0);
1508         }
1509         // refresh the annotation rows
1510         if (updateCalcs)
1511         {
1512           sg.recalcConservation();
1513         }
1514       }
1515     }
1516     oldrfs.clear();
1517   }
1518
1519 }