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