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