fc96f62df9e4865a9f98ad184901cd6f49cf7007
[jalview.git] / src / jalview / viewmodel / AlignmentViewport.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, 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.Conservation;
21 import jalview.api.AlignCalcManagerI;
22 import jalview.api.AlignViewportI;
23 import jalview.api.AlignmentViewPanel;
24 import jalview.datamodel.AlignmentAnnotation;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.AlignmentView;
27 import jalview.datamodel.Annotation;
28 import jalview.datamodel.ColumnSelection;
29 import jalview.datamodel.Sequence;
30 import jalview.datamodel.SequenceGroup;
31 import jalview.datamodel.SequenceI;
32 import jalview.schemes.ClustalxColourScheme;
33 import jalview.schemes.ColourSchemeI;
34 import jalview.schemes.ResidueProperties;
35 import jalview.workers.AlignCalcManager;
36 import jalview.workers.ConsensusThread;
37 import jalview.workers.ConservationThread;
38 import jalview.workers.StrucConsensusThread;
39
40 import java.util.Hashtable;
41 import java.util.Vector;
42
43 /**
44  * base class holding visualization and analysis attributes and common logic for
45  * an active alignment view displayed in the GUI
46  * 
47  * @author jimp
48  * 
49  */
50 public abstract class AlignmentViewport implements AlignViewportI
51 {
52   /**
53    * alignment displayed in the viewport. Please use get/setter
54    */
55   protected AlignmentI alignment;
56
57   protected String sequenceSetID;
58
59   /**
60    * probably unused indicator that view is of a dataset rather than an
61    * alignment
62    */
63   protected boolean isDataset = false;
64
65   private Hashtable hiddenRepSequences;
66
67   protected ColumnSelection colSel = new ColumnSelection();
68
69   public boolean autoCalculateConsensus = true;
70
71   protected boolean autoCalculateStrucConsensus = true;
72
73   protected boolean ignoreGapsInConsensusCalculation = false;
74
75   protected ColourSchemeI globalColourScheme = null;
76
77
78   public void setGlobalColourScheme(ColourSchemeI cs)
79   {
80     globalColourScheme = cs;
81   }
82
83   public ColourSchemeI getGlobalColourScheme()
84   {
85     return globalColourScheme;
86   }
87
88   protected AlignmentAnnotation consensus;
89
90   protected AlignmentAnnotation strucConsensus;
91
92   protected AlignmentAnnotation conservation;
93
94   protected AlignmentAnnotation quality;
95
96   protected AlignmentAnnotation[] groupConsensus;
97
98   protected AlignmentAnnotation[] groupConservation;
99
100   /**
101    * results of alignment consensus analysis for visible portion of view
102    */
103   protected Hashtable[] hconsensus = null;
104
105   /**
106    * results of secondary structure base pair consensus for visible portion of
107    * view
108    */
109   protected Hashtable[] hStrucConsensus = null;
110
111   /**
112    * percentage gaps allowed in a column before all amino acid properties should
113    * be considered unconserved
114    */
115   int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
116
117   public int getConsPercGaps()
118   {
119     return ConsPercGaps;
120   }
121   @Override
122   public void setSequenceConsensusHash(Hashtable[] hconsensus)
123   {
124     this.hconsensus = hconsensus;
125
126   }
127
128   @Override
129   public Hashtable[] getSequenceConsensusHash()
130   {
131     return hconsensus;
132   }
133
134   @Override
135   public Hashtable[] getRnaStructureConsensusHash()
136   {
137     return hStrucConsensus;
138   }
139
140   @Override
141   public void setRnaStructureConsensusHash(Hashtable[] hStrucConsensus)
142   {
143     this.hStrucConsensus = hStrucConsensus;
144
145   }
146
147   @Override
148   public AlignmentAnnotation getAlignmentQualityAnnot()
149   {
150     return quality;
151   }
152
153   @Override
154   public AlignmentAnnotation getAlignmentConservationAnnotation()
155   {
156     return conservation;
157   }
158
159   @Override
160   public AlignmentAnnotation getAlignmentConsensusAnnotation()
161   {
162     return consensus;
163   }
164
165   @Override
166   public AlignmentAnnotation getAlignmentStrucConsensusAnnotation()
167   {
168     return strucConsensus;
169   }
170
171   protected AlignCalcManagerI calculator = new AlignCalcManager();
172
173   /**
174    * trigger update of conservation annotation
175    */
176   public void updateConservation(final AlignmentViewPanel ap)
177   {
178     // see note in mantis : issue number 8585
179     if (alignment.isNucleotide() || conservation == null
180             || !autoCalculateConsensus)
181     {
182       return;
183     }
184     if (!calculator
185             .startRegisteredWorkersOfClass(jalview.workers.ConservationThread.class))
186     {
187       calculator.registerWorker(new jalview.workers.ConservationThread(
188               this, ap));
189     }
190   }
191
192   /**
193    * trigger update of consensus annotation
194    */
195   public void updateConsensus(final AlignmentViewPanel ap)
196   {
197     // see note in mantis : issue number 8585
198     if (consensus == null || !autoCalculateConsensus)
199     {
200       return;
201     }
202     if (!calculator.startRegisteredWorkersOfClass(ConsensusThread.class))
203     {
204       calculator.registerWorker(new ConsensusThread(this, ap));
205     }
206   }
207
208   // --------START Structure Conservation
209   public void updateStrucConsensus(final AlignmentViewPanel ap)
210   {
211     if (autoCalculateStrucConsensus && strucConsensus == null
212             && alignment.isNucleotide() && alignment.hasRNAStructure())
213     {
214
215     }
216
217     // see note in mantis : issue number 8585
218     if (strucConsensus == null || !autoCalculateStrucConsensus)
219     {
220       return;
221     }
222     if (!calculator
223             .startRegisteredWorkersOfClass(StrucConsensusThread.class))
224     {
225       calculator.registerWorker(new StrucConsensusThread(this, ap));
226     }
227   }
228
229   public boolean isCalcInProgress()
230   {
231     return calculator.isWorking();
232   }
233
234   public boolean isCalculationInProgress(
235           AlignmentAnnotation alignmentAnnotation)
236   {
237     if (!alignmentAnnotation.autoCalculated)
238       return false;
239     if (calculator.workingInvolvedWith(alignmentAnnotation))
240     {
241       // System.err.println("grey out ("+alignmentAnnotation.label+")");
242       return true;
243     }
244     return false;
245   }
246
247   @Override
248   public boolean isClosed()
249   {
250     // TODO: check that this isClosed is only true after panel is closed, not
251     // before it is fully constructed.
252     return alignment == null;
253   }
254
255   @Override
256   public AlignCalcManagerI getCalcManager()
257   {
258     return calculator;
259   }
260
261   /**
262    * should conservation rows be shown for groups
263    */
264   protected boolean showGroupConservation = false;
265
266   /**
267    * should consensus rows be shown for groups
268    */
269   protected boolean showGroupConsensus = false;
270
271   /**
272    * should consensus profile be rendered by default
273    */
274   protected boolean showSequenceLogo = false;
275   /**
276    * should consensus profile be rendered normalised to row height
277    */
278   protected boolean normaliseSequenceLogo = false;
279   /**
280    * should consensus histograms be rendered by default
281    */
282   protected boolean showConsensusHistogram = true;
283
284   /**
285    * @return the showConsensusProfile
286    */
287   public boolean isShowSequenceLogo()
288   {
289     return showSequenceLogo;
290   }
291
292   /**
293    * @param showSequenceLogo
294    *          the new value
295    */
296   public void setShowSequenceLogo(boolean showSequenceLogo)
297   {
298     if (showSequenceLogo != this.showSequenceLogo)
299     {
300       // TODO: decouple settings setting from calculation when refactoring
301       // annotation update method from alignframe to viewport
302       this.showSequenceLogo = showSequenceLogo;
303       calculator.updateAnnotationFor(ConsensusThread.class);
304       calculator.updateAnnotationFor(StrucConsensusThread.class);
305     }
306     this.showSequenceLogo = showSequenceLogo;
307   }
308
309   /**
310    * @param showConsensusHistogram
311    *          the showConsensusHistogram to set
312    */
313   public void setShowConsensusHistogram(boolean showConsensusHistogram)
314   {
315     this.showConsensusHistogram = showConsensusHistogram;
316   }
317
318   /**
319    * @return the showGroupConservation
320    */
321   public boolean isShowGroupConservation()
322   {
323     return showGroupConservation;
324   }
325
326   /**
327    * @param showGroupConservation
328    *          the showGroupConservation to set
329    */
330   public void setShowGroupConservation(boolean showGroupConservation)
331   {
332     this.showGroupConservation = showGroupConservation;
333   }
334
335   /**
336    * @return the showGroupConsensus
337    */
338   public boolean isShowGroupConsensus()
339   {
340     return showGroupConsensus;
341   }
342
343   /**
344    * @param showGroupConsensus
345    *          the showGroupConsensus to set
346    */
347   public void setShowGroupConsensus(boolean showGroupConsensus)
348   {
349     this.showGroupConsensus = showGroupConsensus;
350   }
351
352   /**
353    * 
354    * @return flag to indicate if the consensus histogram should be rendered by
355    *         default
356    */
357   public boolean isShowConsensusHistogram()
358   {
359     return this.showConsensusHistogram;
360   }
361
362   /**
363    * show non-conserved residues only
364    */
365   protected boolean showUnconserved = false;
366
367
368   /**
369    * when set, updateAlignment will always ensure sequences are of equal length
370    */
371   private boolean padGaps = false;
372
373   /**
374    * when set, alignment should be reordered according to a newly opened tree
375    */
376   public boolean sortByTree = false;
377
378   public boolean getShowUnconserved()
379   {
380     return showUnconserved;
381   }
382
383   public void setShowUnconserved(boolean showunconserved)
384   {
385     showUnconserved = showunconserved;
386   }
387
388   /**
389    * @param showNonconserved
390    *          the showUnconserved to set
391    */
392   public void setShowunconserved(boolean displayNonconserved)
393   {
394     this.showUnconserved = displayNonconserved;
395   }
396
397   /**
398    * 
399    * 
400    * @return null or the currently selected sequence region
401    */
402   public SequenceGroup getSelectionGroup()
403   {
404     return selectionGroup;
405   }
406
407   /**
408    * Set the selection group for this window.
409    * 
410    * @param sg
411    *          - group holding references to sequences in this alignment view
412    * 
413    */
414   public void setSelectionGroup(SequenceGroup sg)
415   {
416     selectionGroup = sg;
417   }
418
419   public void setHiddenColumns(ColumnSelection colsel)
420   {
421     this.colSel = colsel;
422     if (colSel.getHiddenColumns() != null)
423     {
424       hasHiddenColumns = true;
425     }
426   }
427
428   public ColumnSelection getColumnSelection()
429   {
430     return colSel;
431   }
432
433   public void setColumnSelection(ColumnSelection colSel)
434   {
435     this.colSel = colSel;
436   }
437   public Hashtable getHiddenRepSequences()
438   {
439     return hiddenRepSequences;
440   }
441   public void setHiddenRepSequences(Hashtable hiddenRepSequences)
442   {
443     this.hiddenRepSequences = hiddenRepSequences;
444   }
445   protected boolean hasHiddenColumns = false;
446
447   public void updateHiddenColumns()
448   {
449     hasHiddenColumns = colSel.getHiddenColumns() != null;
450   }
451
452   protected boolean hasHiddenRows = false;
453
454   public boolean hasHiddenRows()
455   {
456     return hasHiddenRows;
457   }
458
459   protected SequenceGroup selectionGroup;
460
461   public void setSequenceSetId(String newid)
462   {
463     if (sequenceSetID != null)
464     {
465       System.err
466               .println("Warning - overwriting a sequenceSetId for a viewport!");
467     }
468     sequenceSetID = new String(newid);
469   }
470
471   public String getSequenceSetId()
472   {
473     if (sequenceSetID == null)
474     {
475       sequenceSetID = alignment.hashCode() + "";
476     }
477
478     return sequenceSetID;
479   }
480
481   /**
482    * unique viewId for synchronizing state (e.g. with stored Jalview Project)
483    * 
484    */
485   protected String viewId = null;
486
487   public String getViewId()
488   {
489     if (viewId == null)
490     {
491       viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
492     }
493     return viewId;
494   }
495
496   public void setIgnoreGapsConsensus(boolean b, AlignmentViewPanel ap)
497   {
498     ignoreGapsInConsensusCalculation = b;
499     if (ap != null)
500     {
501       updateConsensus(ap);
502       if (globalColourScheme != null)
503       {
504         globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
505                 ignoreGapsInConsensusCalculation);
506       }
507     }
508
509   }
510
511   private long sgrouphash = -1, colselhash = -1;
512
513   /**
514    * checks current SelectionGroup against record of last hash value, and
515    * updates record.
516    * 
517    * @param b
518    *          update the record of last hash value
519    * 
520    * @return true if SelectionGroup changed since last call (when b is true)
521    */
522   public boolean isSelectionGroupChanged(boolean b)
523   {
524     int hc = (selectionGroup == null || selectionGroup.getSize() == 0) ? -1
525             : selectionGroup.hashCode();
526     if (hc != -1 && hc != sgrouphash)
527     {
528       if (b)
529       {
530         sgrouphash = hc;
531       }
532       return true;
533     }
534     return false;
535   }
536
537   /**
538    * checks current colsel against record of last hash value, and optionally
539    * updates record.
540    * 
541    * @param b
542    *          update the record of last hash value
543    * @return true if colsel changed since last call (when b is true)
544    */
545   public boolean isColSelChanged(boolean b)
546   {
547     int hc = (colSel == null || colSel.size() == 0) ? -1 : colSel
548             .hashCode();
549     if (hc != -1 && hc != colselhash)
550     {
551       if (b)
552       {
553         colselhash = hc;
554       }
555       return true;
556     }
557     return false;
558   }
559
560   public boolean getIgnoreGapsConsensus()
561   {
562     return ignoreGapsInConsensusCalculation;
563   }
564
565   // / property change stuff
566
567   // JBPNote Prolly only need this in the applet version.
568   private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
569           this);
570
571   protected boolean showConservation = true;
572
573   protected boolean showQuality = true;
574
575   protected boolean showConsensus = true;
576
577
578   /**
579    * Property change listener for changes in alignment
580    * 
581    * @param listener
582    *          DOCUMENT ME!
583    */
584   public void addPropertyChangeListener(
585           java.beans.PropertyChangeListener listener)
586   {
587     changeSupport.addPropertyChangeListener(listener);
588   }
589
590   /**
591    * DOCUMENT ME!
592    * 
593    * @param listener
594    *          DOCUMENT ME!
595    */
596   public void removePropertyChangeListener(
597           java.beans.PropertyChangeListener listener)
598   {
599     changeSupport.removePropertyChangeListener(listener);
600   }
601
602   /**
603    * Property change listener for changes in alignment
604    * 
605    * @param prop
606    *          DOCUMENT ME!
607    * @param oldvalue
608    *          DOCUMENT ME!
609    * @param newvalue
610    *          DOCUMENT ME!
611    */
612   public void firePropertyChange(String prop, Object oldvalue,
613           Object newvalue)
614   {
615     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
616   }
617
618   // common hide/show column stuff
619   
620
621   public void hideSelectedColumns()
622   {
623     if (colSel.size() < 1)
624     {
625       return;
626     }
627
628     colSel.hideSelectedColumns();
629     setSelectionGroup(null);
630
631     hasHiddenColumns = true;
632   }
633
634   public void hideColumns(int start, int end)
635   {
636     if (start == end)
637     {
638       colSel.hideColumns(start);
639     }
640     else
641     {
642       colSel.hideColumns(start, end);
643     }
644
645     hasHiddenColumns = true;
646   }
647
648   public void showColumn(int col)
649   {
650     colSel.revealHiddenColumns(col);
651     if (colSel.getHiddenColumns() == null)
652     {
653       hasHiddenColumns = false;
654     }
655   }
656
657   public void showAllHiddenColumns()
658   {
659     colSel.revealAllHiddenColumns();
660     hasHiddenColumns = false;
661   }
662
663   
664   // common hide/show seq stuff
665   public void showAllHiddenSeqs()
666   {
667     if (alignment.getHiddenSequences().getSize() > 0)
668     {
669       if (selectionGroup == null)
670       {
671         selectionGroup = new SequenceGroup();
672         selectionGroup.setEndRes(alignment.getWidth() - 1);
673       }
674       Vector tmp = alignment.getHiddenSequences().showAll(
675               hiddenRepSequences);
676       for (int t = 0; t < tmp.size(); t++)
677       {
678         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
679       }
680
681       hasHiddenRows = false;
682       hiddenRepSequences = null;
683
684       firePropertyChange("alignment", null, alignment.getSequences());
685       // used to set hasHiddenRows/hiddenRepSequences here, after the property
686       // changed event
687       sendSelection();
688     }
689   }
690
691   public void showSequence(int index)
692   {
693     Vector tmp = alignment.getHiddenSequences().showSequence(index,
694             hiddenRepSequences);
695     if (tmp.size() > 0)
696     {
697       if (selectionGroup == null)
698       {
699         selectionGroup = new SequenceGroup();
700         selectionGroup.setEndRes(alignment.getWidth() - 1);
701       }
702
703       for (int t = 0; t < tmp.size(); t++)
704       {
705         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
706       }
707       // JBPNote: refactor: only update flag if we modified visiblity (used to
708       // do this regardless)
709       if (alignment.getHiddenSequences().getSize() < 1)
710       {
711         hasHiddenRows = false;
712       }
713       firePropertyChange("alignment", null, alignment.getSequences());
714       sendSelection();
715     }
716   }
717
718
719   
720   public void hideAllSelectedSeqs()
721   {
722     if (selectionGroup == null || selectionGroup.getSize() < 1)
723     {
724       return;
725     }
726
727     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
728
729     hideSequence(seqs);
730
731     setSelectionGroup(null);
732   }
733   
734
735   public void hideSequence(SequenceI[] seq)
736   {
737     if (seq != null)
738     {
739       for (int i = 0; i < seq.length; i++)
740       {
741         alignment.getHiddenSequences().hideSequence(seq[i]);
742       }
743       hasHiddenRows = true;
744       firePropertyChange("alignment", null, alignment.getSequences());
745     }
746   }
747
748   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
749   {
750     int sSize = sg.getSize();
751     if (sSize < 2)
752     {
753       return;
754     }
755
756     if (hiddenRepSequences == null)
757     {
758       hiddenRepSequences = new Hashtable();
759     }
760
761     hiddenRepSequences.put(repSequence, sg);
762
763     // Hide all sequences except the repSequence
764     SequenceI[] seqs = new SequenceI[sSize - 1];
765     int index = 0;
766     for (int i = 0; i < sSize; i++)
767     {
768       if (sg.getSequenceAt(i) != repSequence)
769       {
770         if (index == sSize - 1)
771         {
772           return;
773         }
774
775         seqs[index++] = sg.getSequenceAt(i);
776       }
777     }
778     sg.setSeqrep(repSequence); // note: not done in 2.7applet
779     sg.setHidereps(true); // note: not done in 2.7applet
780     hideSequence(seqs);
781
782   }
783
784   public boolean isHiddenRepSequence(SequenceI seq)
785   {
786     return hiddenRepSequences != null
787             && hiddenRepSequences.containsKey(seq);
788   }
789
790   public SequenceGroup getRepresentedSequences(SequenceI seq)
791   {
792     return (SequenceGroup) (hiddenRepSequences == null ? null
793             : hiddenRepSequences.get(seq));
794   }
795
796   public int adjustForHiddenSeqs(int alignmentIndex)
797   {
798     return alignment.getHiddenSequences().adjustForHiddenSeqs(
799             alignmentIndex);
800   }
801
802   // Selection manipulation
803   /**
804    * broadcast selection to any interested parties
805    */
806   public abstract void sendSelection();
807   
808
809   public void invertColumnSelection()
810   {
811     colSel.invertColumnSelection(0, alignment.getWidth());
812   }
813
814   /**
815    * This method returns an array of new SequenceI objects derived from the
816    * whole alignment or just the current selection with start and end points
817    * adjusted
818    * 
819    * @note if you need references to the actual SequenceI objects in the
820    *       alignment or currently selected then use getSequenceSelection()
821    * @return selection as new sequenceI objects
822    */
823   public SequenceI[] getSelectionAsNewSequence()
824   {
825     SequenceI[] sequences;
826     // JBPNote: Need to test jalviewLite.getSelectedSequencesAsAlignmentFrom -
827     // this was the only caller in the applet for this method
828     // JBPNote: in applet, this method returned references to the alignment
829     // sequences, and it did not honour the presence/absence of annotation
830     // attached to the alignment (probably!)
831     if (selectionGroup == null)
832     {
833       sequences = alignment.getSequencesArray();
834       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
835       for (int i = 0; i < sequences.length; i++)
836       {
837         sequences[i] = new Sequence(sequences[i], annots); // construct new
838         // sequence with
839         // subset of visible
840         // annotation
841       }
842     }
843     else
844     {
845       sequences = selectionGroup.getSelectionAsNewSequences(alignment);
846     }
847
848     return sequences;
849   }
850
851   /**
852    * get the currently selected sequence objects or all the sequences in the
853    * alignment.
854    * 
855    * @return array of references to sequence objects
856    */
857   public SequenceI[] getSequenceSelection()
858   {
859     SequenceI[] sequences = null;
860     if (selectionGroup != null)
861     {
862       sequences = selectionGroup.getSequencesInOrder(alignment);
863     }
864     if (sequences == null)
865     {
866       sequences = alignment.getSequencesArray();
867     }
868     return sequences;
869   }
870
871
872   /**
873    * This method returns the visible alignment as text, as seen on the GUI, ie
874    * if columns are hidden they will not be returned in the result. Use this for
875    * calculating trees, PCA, redundancy etc on views which contain hidden
876    * columns.
877    * 
878    * @return String[]
879    */
880   public jalview.datamodel.CigarArray getViewAsCigars(
881           boolean selectedRegionOnly)
882   {
883     return new jalview.datamodel.CigarArray(alignment,
884             (hasHiddenColumns ? colSel : null),
885             (selectedRegionOnly ? selectionGroup : null));
886   }
887
888   /**
889    * return a compact representation of the current alignment selection to pass
890    * to an analysis function
891    * 
892    * @param selectedOnly
893    *          boolean true to just return the selected view
894    * @return AlignmentView
895    */
896   public jalview.datamodel.AlignmentView getAlignmentView(
897           boolean selectedOnly)
898   {
899     return getAlignmentView(selectedOnly, false);
900   }
901
902   /**
903    * return a compact representation of the current alignment selection to pass
904    * to an analysis function
905    * 
906    * @param selectedOnly
907    *          boolean true to just return the selected view
908    * @param markGroups
909    *          boolean true to annotate the alignment view with groups on the
910    *          alignment (and intersecting with selected region if selectedOnly
911    *          is true)
912    * @return AlignmentView
913    */
914   public jalview.datamodel.AlignmentView getAlignmentView(
915           boolean selectedOnly, boolean markGroups)
916   {
917     return new AlignmentView(alignment, colSel, selectionGroup,
918             hasHiddenColumns, selectedOnly, markGroups);
919   }
920
921
922   /**
923    * This method returns the visible alignment as text, as seen on the GUI, ie
924    * if columns are hidden they will not be returned in the result. Use this for
925    * calculating trees, PCA, redundancy etc on views which contain hidden
926    * columns.
927    * 
928    * @return String[]
929    */
930   public String[] getViewAsString(boolean selectedRegionOnly)
931   {
932     String[] selection = null;
933     SequenceI[] seqs = null;
934     int i, iSize;
935     int start = 0, end = 0;
936     if (selectedRegionOnly && selectionGroup != null)
937     {
938       iSize = selectionGroup.getSize();
939       seqs = selectionGroup.getSequencesInOrder(alignment);
940       start = selectionGroup.getStartRes();
941       end = selectionGroup.getEndRes() + 1;
942     }
943     else
944     {
945       iSize = alignment.getHeight();
946       seqs = alignment.getSequencesArray();
947       end = alignment.getWidth();
948     }
949
950     selection = new String[iSize];
951     if (hasHiddenColumns)
952     {
953       selection = colSel.getVisibleSequenceStrings(start, end, seqs);
954     }
955     else
956     {
957       for (i = 0; i < iSize; i++)
958       {
959         selection[i] = seqs[i].getSequenceAsString(start, end);
960       }
961
962     }
963     return selection;
964   }
965
966   /**
967    * return visible region boundaries within given column range
968    * 
969    * @param min
970    *          first column (inclusive, from 0)
971    * @param max
972    *          last column (exclusive)
973    * @return int[][] range of {start,end} visible positions
974    */
975   public int[][] getVisibleRegionBoundaries(int min, int max)
976   {
977     Vector regions = new Vector();
978     int start = min;
979     int end = max;
980
981     do
982     {
983       if (hasHiddenColumns)
984       {
985         if (start == 0)
986         {
987           start = colSel.adjustForHiddenColumns(start);
988         }
989
990         end = colSel.getHiddenBoundaryRight(start);
991         if (start == end)
992         {
993           end = max;
994         }
995         if (end > max)
996         {
997           end = max;
998         }
999       }
1000
1001       regions.addElement(new int[]
1002       { start, end });
1003
1004       if (hasHiddenColumns)
1005       {
1006         start = colSel.adjustForHiddenColumns(end);
1007         start = colSel.getHiddenBoundaryLeft(start) + 1;
1008       }
1009     } while (end < max);
1010
1011     int[][] startEnd = new int[regions.size()][2];
1012
1013     regions.copyInto(startEnd);
1014
1015     return startEnd;
1016
1017   }
1018   /**
1019    * @return the padGaps
1020    */
1021   public boolean isPadGaps()
1022   {
1023     return padGaps;
1024   }
1025
1026   /**
1027    * @param padGaps
1028    *          the padGaps to set
1029    */
1030   public void setPadGaps(boolean padGaps)
1031   {
1032     this.padGaps = padGaps;
1033   }
1034
1035   /**
1036    * apply any post-edit constraints and trigger any calculations needed after
1037    * an edit has been performed on the alignment
1038    * 
1039    * @param ap
1040    */
1041   public void alignmentChanged(AlignmentViewPanel ap)
1042   {
1043     if (isPadGaps())
1044     {
1045       alignment.padGaps();
1046     }
1047     if (autoCalculateConsensus)
1048     {
1049       updateConsensus(ap);
1050     }
1051     if (hconsensus != null && autoCalculateConsensus)
1052     {
1053       updateConservation(ap);
1054     }
1055     if (autoCalculateStrucConsensus)
1056     {
1057       updateStrucConsensus(ap);
1058     }
1059
1060     // Reset endRes of groups if beyond alignment width
1061     int alWidth = alignment.getWidth();
1062     Vector groups = alignment.getGroups();
1063     if (groups != null)
1064     {
1065       for (int i = 0; i < groups.size(); i++)
1066       {
1067         SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
1068         if (sg.getEndRes() > alWidth)
1069         {
1070           sg.setEndRes(alWidth - 1);
1071         }
1072       }
1073     }
1074
1075     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1076     {
1077       selectionGroup.setEndRes(alWidth - 1);
1078     }
1079
1080     resetAllColourSchemes();
1081     calculator.restartWorkers();
1082     // alignment.adjustSequenceAnnotations();
1083   }
1084
1085   
1086   /**
1087    * reset scope and do calculations for all applied colourschemes on alignment
1088    */
1089   void resetAllColourSchemes()
1090   {
1091     ColourSchemeI cs = globalColourScheme;
1092     if (cs != null)
1093     {
1094       cs.alignmentChanged(alignment);
1095       // TODO: fold all recalc events for clustalX into alignmentChanged
1096       if (cs instanceof ClustalxColourScheme)
1097       {
1098         ((ClustalxColourScheme) cs).resetClustalX(alignment.getSequences(),
1099                 alignment.getWidth());
1100       }
1101
1102       cs.setConsensus(hconsensus);
1103       if (cs.conservationApplied())
1104       {
1105         cs.setConservation(Conservation.calculateConservation("All",
1106                 ResidueProperties.propHash, 3, alignment.getSequences(), 0,
1107                 alignment.getWidth(), false, getConsPercGaps(), false));
1108       }
1109     }
1110
1111     int s, sSize = alignment.getGroups().size();
1112     for (s = 0; s < sSize; s++)
1113     {
1114       SequenceGroup sg = (SequenceGroup) alignment.getGroups().elementAt(s);
1115       if (sg.cs != null && sg.cs instanceof ClustalxColourScheme)
1116       {
1117         ((ClustalxColourScheme) sg.cs).resetClustalX(sg
1118                 .getSequences(hiddenRepSequences), sg.getWidth());
1119       }
1120       sg.recalcConservation();
1121     }
1122   }
1123
1124   protected void initAutoAnnotation()
1125   {
1126     // TODO: add menu option action that nulls or creates consensus object
1127     // depending on if the user wants to see the annotation or not in a
1128     // specific alignment
1129
1130     if (hconsensus == null && !isDataset)
1131     {
1132       if (!alignment.isNucleotide())
1133       {
1134         if (showConservation)
1135         {
1136           if (conservation == null)
1137           {
1138             conservation = new AlignmentAnnotation("Conservation",
1139                     "Conservation of total alignment less than "
1140                             + getConsPercGaps() + "% gaps",
1141                     new Annotation[1], 0f, 11f,
1142                     AlignmentAnnotation.BAR_GRAPH);
1143             conservation.hasText = true;
1144             conservation.autoCalculated = true;
1145             alignment.addAnnotation(conservation);
1146           }
1147         }
1148         if (showQuality)
1149         {
1150           if (quality == null)
1151           {
1152             quality = new AlignmentAnnotation("Quality",
1153                     "Alignment Quality based on Blosum62 scores",
1154                     new Annotation[1], 0f, 11f,
1155                     AlignmentAnnotation.BAR_GRAPH);
1156             quality.hasText = true;
1157             quality.autoCalculated = true;
1158             alignment.addAnnotation(quality);
1159           }
1160         }
1161       }
1162       else
1163       {
1164         if (alignment.hasRNAStructure())
1165         {
1166           strucConsensus = new AlignmentAnnotation("StrucConsensus", "PID",
1167                   new Annotation[1], 0f, 100f,
1168                   AlignmentAnnotation.BAR_GRAPH);
1169           strucConsensus.hasText = true;
1170           strucConsensus.autoCalculated = true;
1171         }
1172       }
1173
1174       consensus = new AlignmentAnnotation("Consensus", "PID",
1175               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
1176       consensus.hasText = true;
1177       consensus.autoCalculated = true;
1178
1179       if (showConsensus)
1180       {
1181         alignment.addAnnotation(consensus);
1182         if (strucConsensus != null)
1183         {
1184           alignment.addAnnotation(strucConsensus);
1185         }
1186       }
1187     }
1188   }
1189
1190 }