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