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