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