JAL-1683 replace year/version strings with tokens in source
[jalview.git] / src / jalview / viewmodel / AlignmentViewport.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ 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   }
641
642   @Override
643   public ColumnSelection getColumnSelection()
644   {
645     return colSel;
646   }
647
648   @Override
649   public void setColumnSelection(ColumnSelection colSel)
650   {
651     this.colSel = colSel;
652     if (colSel != null)
653     {
654       updateHiddenColumns();
655     }
656   }
657
658   /**
659    * 
660    * @return
661    */
662   @Override
663   public Map<SequenceI, SequenceCollectionI> getHiddenRepSequences()
664   {
665     return hiddenRepSequences;
666   }
667
668   @Override
669   public void setHiddenRepSequences(
670           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
671   {
672     this.hiddenRepSequences = hiddenRepSequences;
673   }
674
675   @Override
676   public boolean hasHiddenColumns()
677   {
678     return colSel != null && colSel.hasHiddenColumns();
679   }
680
681   public void updateHiddenColumns()
682   {
683     // this method doesn't really do anything now. But - it could, since a
684     // column Selection could be in the process of modification
685     // hasHiddenColumns = colSel.hasHiddenColumns();
686   }
687
688   protected boolean hasHiddenRows = false;
689
690   @Override
691   public boolean hasHiddenRows()
692   {
693     return hasHiddenRows;
694   }
695
696   protected SequenceGroup selectionGroup;
697
698   public void setSequenceSetId(String newid)
699   {
700     if (sequenceSetID != null)
701     {
702       System.err
703               .println("Warning - overwriting a sequenceSetId for a viewport!");
704     }
705     sequenceSetID = new String(newid);
706   }
707
708   @Override
709   public String getSequenceSetId()
710   {
711     if (sequenceSetID == null)
712     {
713       sequenceSetID = alignment.hashCode() + "";
714     }
715
716     return sequenceSetID;
717   }
718
719   /**
720    * unique viewId for synchronizing state (e.g. with stored Jalview Project)
721    * 
722    */
723   protected String viewId = null;
724
725   public String getViewId()
726   {
727     if (viewId == null)
728     {
729       viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
730     }
731     return viewId;
732   }
733
734   public void setIgnoreGapsConsensus(boolean b, AlignmentViewPanel ap)
735   {
736     ignoreGapsInConsensusCalculation = b;
737     if (ap != null)
738     {
739       updateConsensus(ap);
740       if (globalColourScheme != null)
741       {
742         globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
743                 ignoreGapsInConsensusCalculation);
744       }
745     }
746
747   }
748
749   private long sgrouphash = -1, colselhash = -1;
750
751   /**
752    * checks current SelectionGroup against record of last hash value, and
753    * updates record.
754    * 
755    * @param b
756    *          update the record of last hash value
757    * 
758    * @return true if SelectionGroup changed since last call (when b is true)
759    */
760   public boolean isSelectionGroupChanged(boolean b)
761   {
762     int hc = (selectionGroup == null || selectionGroup.getSize() == 0) ? -1
763             : selectionGroup.hashCode();
764     if (hc != -1 && hc != sgrouphash)
765     {
766       if (b)
767       {
768         sgrouphash = hc;
769       }
770       return true;
771     }
772     return false;
773   }
774
775   /**
776    * checks current colsel against record of last hash value, and optionally
777    * updates record.
778    * 
779    * @param b
780    *          update the record of last hash value
781    * @return true if colsel changed since last call (when b is true)
782    */
783   public boolean isColSelChanged(boolean b)
784   {
785     int hc = (colSel == null || colSel.size() == 0) ? -1 : colSel
786             .hashCode();
787     if (hc != -1 && hc != colselhash)
788     {
789       if (b)
790       {
791         colselhash = hc;
792       }
793       return true;
794     }
795     return false;
796   }
797
798   @Override
799   public boolean getIgnoreGapsConsensus()
800   {
801     return ignoreGapsInConsensusCalculation;
802   }
803
804   // / property change stuff
805
806   // JBPNote Prolly only need this in the applet version.
807   private final java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
808           this);
809
810   protected boolean showConservation = true;
811
812   protected boolean showQuality = true;
813
814   protected boolean showConsensus = true;
815
816   Hashtable sequenceColours;
817
818   /**
819    * Property change listener for changes in alignment
820    * 
821    * @param listener
822    *          DOCUMENT ME!
823    */
824   public void addPropertyChangeListener(
825           java.beans.PropertyChangeListener listener)
826   {
827     changeSupport.addPropertyChangeListener(listener);
828   }
829
830   /**
831    * DOCUMENT ME!
832    * 
833    * @param listener
834    *          DOCUMENT ME!
835    */
836   public void removePropertyChangeListener(
837           java.beans.PropertyChangeListener listener)
838   {
839     changeSupport.removePropertyChangeListener(listener);
840   }
841
842   /**
843    * Property change listener for changes in alignment
844    * 
845    * @param prop
846    *          DOCUMENT ME!
847    * @param oldvalue
848    *          DOCUMENT ME!
849    * @param newvalue
850    *          DOCUMENT ME!
851    */
852   public void firePropertyChange(String prop, Object oldvalue,
853           Object newvalue)
854   {
855     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
856   }
857
858   // common hide/show column stuff
859
860   public void hideSelectedColumns()
861   {
862     if (colSel.size() < 1)
863     {
864       return;
865     }
866
867     colSel.hideSelectedColumns();
868     setSelectionGroup(null);
869
870   }
871
872   public void hideColumns(int start, int end)
873   {
874     if (start == end)
875     {
876       colSel.hideColumns(start);
877     }
878     else
879     {
880       colSel.hideColumns(start, end);
881     }
882   }
883
884   public void showColumn(int col)
885   {
886     colSel.revealHiddenColumns(col);
887
888   }
889
890   public void showAllHiddenColumns()
891   {
892     colSel.revealAllHiddenColumns();
893   }
894
895   // common hide/show seq stuff
896   public void showAllHiddenSeqs()
897   {
898     if (alignment.getHiddenSequences().getSize() > 0)
899     {
900       if (selectionGroup == null)
901       {
902         selectionGroup = new SequenceGroup();
903         selectionGroup.setEndRes(alignment.getWidth() - 1);
904       }
905       List<SequenceI> tmp = alignment.getHiddenSequences().showAll(
906               hiddenRepSequences);
907       for (SequenceI seq : tmp)
908       {
909         selectionGroup.addSequence(seq, false);
910         setSequenceAnnotationsVisible(seq, true);
911       }
912
913       hasHiddenRows = false;
914       hiddenRepSequences = null;
915
916       firePropertyChange("alignment", null, alignment.getSequences());
917       // used to set hasHiddenRows/hiddenRepSequences here, after the property
918       // changed event
919       sendSelection();
920     }
921   }
922
923   public void showSequence(int index)
924   {
925     List<SequenceI> tmp = alignment.getHiddenSequences().showSequence(
926             index,
927             hiddenRepSequences);
928     if (tmp.size() > 0)
929     {
930       if (selectionGroup == null)
931       {
932         selectionGroup = new SequenceGroup();
933         selectionGroup.setEndRes(alignment.getWidth() - 1);
934       }
935
936       for (SequenceI seq : tmp)
937       {
938         selectionGroup.addSequence(seq, false);
939         setSequenceAnnotationsVisible(seq, true);
940       }
941       // JBPNote: refactor: only update flag if we modified visiblity (used to
942       // do this regardless)
943       if (alignment.getHiddenSequences().getSize() < 1)
944       {
945         hasHiddenRows = false;
946       }
947       firePropertyChange("alignment", null, alignment.getSequences());
948       sendSelection();
949     }
950   }
951
952   public void hideAllSelectedSeqs()
953   {
954     if (selectionGroup == null || selectionGroup.getSize() < 1)
955     {
956       return;
957     }
958
959     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
960
961     hideSequence(seqs);
962
963     setSelectionGroup(null);
964   }
965
966   public void hideSequence(SequenceI[] seq)
967   {
968     if (seq != null)
969     {
970       for (int i = 0; i < seq.length; i++)
971       {
972         alignment.getHiddenSequences().hideSequence(seq[i]);
973         setSequenceAnnotationsVisible(seq[i], false);
974       }
975       hasHiddenRows = true;
976       firePropertyChange("alignment", null, alignment.getSequences());
977     }
978   }
979
980   /**
981    * Set visibility for any annotations for the given sequence.
982    * 
983    * @param sequenceI
984    */
985   protected void setSequenceAnnotationsVisible(SequenceI sequenceI,
986           boolean visible)
987   {
988     for (AlignmentAnnotation ann : alignment.getAlignmentAnnotation())
989     {
990       if (ann.sequenceRef == sequenceI)
991       {
992         ann.visible = visible;
993       }
994     }
995   }
996
997   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
998   {
999     int sSize = sg.getSize();
1000     if (sSize < 2)
1001     {
1002       return;
1003     }
1004
1005     if (hiddenRepSequences == null)
1006     {
1007       hiddenRepSequences = new Hashtable();
1008     }
1009
1010     hiddenRepSequences.put(repSequence, sg);
1011
1012     // Hide all sequences except the repSequence
1013     SequenceI[] seqs = new SequenceI[sSize - 1];
1014     int index = 0;
1015     for (int i = 0; i < sSize; i++)
1016     {
1017       if (sg.getSequenceAt(i) != repSequence)
1018       {
1019         if (index == sSize - 1)
1020         {
1021           return;
1022         }
1023
1024         seqs[index++] = sg.getSequenceAt(i);
1025       }
1026     }
1027     sg.setSeqrep(repSequence); // note: not done in 2.7applet
1028     sg.setHidereps(true); // note: not done in 2.7applet
1029     hideSequence(seqs);
1030
1031   }
1032
1033   public boolean isHiddenRepSequence(SequenceI seq)
1034   {
1035     return alignment.getSeqrep()==seq || (hiddenRepSequences != null
1036             && hiddenRepSequences.containsKey(seq));
1037   }
1038
1039   public SequenceGroup getRepresentedSequences(SequenceI seq)
1040   {
1041     return (SequenceGroup) (hiddenRepSequences == null ? null
1042             : hiddenRepSequences.get(seq));
1043   }
1044
1045   @Override
1046   public int adjustForHiddenSeqs(int alignmentIndex)
1047   {
1048     return alignment.getHiddenSequences().adjustForHiddenSeqs(
1049             alignmentIndex);
1050   }
1051
1052   @Override
1053   public abstract void sendSelection();
1054
1055   @Override
1056   public void invertColumnSelection()
1057   {
1058     colSel.invertColumnSelection(0, alignment.getWidth());
1059   }
1060
1061
1062   @Override
1063   public SequenceI[] getSelectionAsNewSequence()
1064   {
1065     SequenceI[] sequences;
1066     // JBPNote: Need to test jalviewLite.getSelectedSequencesAsAlignmentFrom -
1067     // this was the only caller in the applet for this method
1068     // JBPNote: in applet, this method returned references to the alignment
1069     // sequences, and it did not honour the presence/absence of annotation
1070     // attached to the alignment (probably!)
1071     if (selectionGroup == null || selectionGroup.getSize() == 0)
1072     {
1073       sequences = alignment.getSequencesArray();
1074       AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
1075       for (int i = 0; i < sequences.length; i++)
1076       {
1077         sequences[i] = new Sequence(sequences[i], annots); // construct new
1078         // sequence with
1079         // subset of visible
1080         // annotation
1081       }
1082     }
1083     else
1084     {
1085       sequences = selectionGroup.getSelectionAsNewSequences(alignment);
1086     }
1087
1088     return sequences;
1089   }
1090
1091
1092   @Override
1093   public SequenceI[] getSequenceSelection()
1094   {
1095     SequenceI[] sequences = null;
1096     if (selectionGroup != null)
1097     {
1098       sequences = selectionGroup.getSequencesInOrder(alignment);
1099     }
1100     if (sequences == null)
1101     {
1102       sequences = alignment.getSequencesArray();
1103     }
1104     return sequences;
1105   }
1106
1107
1108   @Override
1109   public jalview.datamodel.CigarArray getViewAsCigars(
1110           boolean selectedRegionOnly)
1111   {
1112     return new jalview.datamodel.CigarArray(alignment, colSel,
1113             (selectedRegionOnly ? selectionGroup : null));
1114   }
1115
1116
1117   @Override
1118   public jalview.datamodel.AlignmentView getAlignmentView(
1119           boolean selectedOnly)
1120   {
1121     return getAlignmentView(selectedOnly, false);
1122   }
1123
1124
1125   @Override
1126   public jalview.datamodel.AlignmentView getAlignmentView(
1127           boolean selectedOnly, boolean markGroups)
1128   {
1129     return new AlignmentView(alignment, colSel, selectionGroup,
1130             colSel != null && colSel.hasHiddenColumns(), selectedOnly,
1131             markGroups);
1132   }
1133
1134
1135   @Override
1136   public String[] getViewAsString(boolean selectedRegionOnly)
1137   {
1138     String[] selection = null;
1139     SequenceI[] seqs = null;
1140     int i, iSize;
1141     int start = 0, end = 0;
1142     if (selectedRegionOnly && selectionGroup != null)
1143     {
1144       iSize = selectionGroup.getSize();
1145       seqs = selectionGroup.getSequencesInOrder(alignment);
1146       start = selectionGroup.getStartRes();
1147       end = selectionGroup.getEndRes() + 1;
1148     }
1149     else
1150     {
1151       iSize = alignment.getHeight();
1152       seqs = alignment.getSequencesArray();
1153       end = alignment.getWidth();
1154     }
1155
1156     selection = new String[iSize];
1157     if (colSel != null && colSel.hasHiddenColumns())
1158     {
1159       selection = colSel.getVisibleSequenceStrings(start, end, seqs);
1160     }
1161     else
1162     {
1163       for (i = 0; i < iSize; i++)
1164       {
1165         selection[i] = seqs[i].getSequenceAsString(start, end);
1166       }
1167
1168     }
1169     return selection;
1170   }
1171
1172
1173   @Override
1174   public int[][] getVisibleRegionBoundaries(int min, int max)
1175   {
1176     Vector regions = new Vector();
1177     int start = min;
1178     int end = max;
1179
1180     do
1181     {
1182       if (colSel != null && colSel.hasHiddenColumns())
1183       {
1184         if (start == 0)
1185         {
1186           start = colSel.adjustForHiddenColumns(start);
1187         }
1188
1189         end = colSel.getHiddenBoundaryRight(start);
1190         if (start == end)
1191         {
1192           end = max;
1193         }
1194         if (end > max)
1195         {
1196           end = max;
1197         }
1198       }
1199
1200       regions.addElement(new int[]
1201       { start, end });
1202
1203       if (colSel != null && colSel.hasHiddenColumns())
1204       {
1205         start = colSel.adjustForHiddenColumns(end);
1206         start = colSel.getHiddenBoundaryLeft(start) + 1;
1207       }
1208     } while (end < max);
1209
1210     int[][] startEnd = new int[regions.size()][2];
1211
1212     regions.copyInto(startEnd);
1213
1214     return startEnd;
1215
1216   }
1217
1218   @Override
1219   public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(boolean selectedOnly)
1220   {
1221     ArrayList<AlignmentAnnotation> ala = new ArrayList<AlignmentAnnotation>();
1222     AlignmentAnnotation[] aa;
1223     if ((aa=alignment.getAlignmentAnnotation())!=null)
1224     {
1225       for (AlignmentAnnotation annot:aa)
1226       {
1227         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
1228         if (selectedOnly && selectionGroup!=null)
1229         {
1230           colSel.makeVisibleAnnotation(selectionGroup.getStartRes(), selectionGroup.getEndRes(),clone);
1231         } else {
1232           colSel.makeVisibleAnnotation(clone);
1233         }
1234         ala.add(clone);
1235       }
1236     }
1237     return ala;
1238   }
1239
1240
1241   @Override
1242   public boolean isPadGaps()
1243   {
1244     return padGaps;
1245   }
1246
1247
1248   @Override
1249   public void setPadGaps(boolean padGaps)
1250   {
1251     this.padGaps = padGaps;
1252   }
1253
1254   /**
1255    * apply any post-edit constraints and trigger any calculations needed after
1256    * an edit has been performed on the alignment
1257    * 
1258    * @param ap
1259    */
1260   @Override
1261   public void alignmentChanged(AlignmentViewPanel ap)
1262   {
1263     if (isPadGaps())
1264     {
1265       alignment.padGaps();
1266     }
1267     if (autoCalculateConsensus)
1268     {
1269       updateConsensus(ap);
1270     }
1271     if (hconsensus != null && autoCalculateConsensus)
1272     {
1273       updateConservation(ap);
1274     }
1275     if (autoCalculateStrucConsensus)
1276     {
1277       updateStrucConsensus(ap);
1278     }
1279
1280     // Reset endRes of groups if beyond alignment width
1281     int alWidth = alignment.getWidth();
1282     List<SequenceGroup> groups = alignment.getGroups();
1283     if (groups != null)
1284     {
1285       for (SequenceGroup sg : groups)
1286       {
1287         if (sg.getEndRes() > alWidth)
1288         {
1289           sg.setEndRes(alWidth - 1);
1290         }
1291       }
1292     }
1293
1294     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1295     {
1296       selectionGroup.setEndRes(alWidth - 1);
1297     }
1298
1299     resetAllColourSchemes();
1300     calculator.restartWorkers();
1301     // alignment.adjustSequenceAnnotations();
1302   }
1303
1304   /**
1305    * reset scope and do calculations for all applied colourschemes on alignment
1306    */
1307   void resetAllColourSchemes()
1308   {
1309     ColourSchemeI cs = globalColourScheme;
1310     if (cs != null)
1311     {
1312       cs.alignmentChanged(alignment, hiddenRepSequences);
1313
1314       cs.setConsensus(hconsensus);
1315       if (cs.conservationApplied())
1316       {
1317         cs.setConservation(Conservation.calculateConservation("All",
1318                 ResidueProperties.propHash, 3, alignment.getSequences(), 0,
1319                 alignment.getWidth(), false, getConsPercGaps(), false));
1320       }
1321     }
1322
1323     for (SequenceGroup sg : alignment.getGroups())
1324     {
1325       if (sg.cs != null)
1326       {
1327         sg.cs.alignmentChanged(sg, hiddenRepSequences);
1328       }
1329       sg.recalcConservation();
1330     }
1331   }
1332
1333   protected void initAutoAnnotation()
1334   {
1335     // TODO: add menu option action that nulls or creates consensus object
1336     // depending on if the user wants to see the annotation or not in a
1337     // specific alignment
1338
1339     if (hconsensus == null && !isDataset)
1340     {
1341       if (!alignment.isNucleotide())
1342       {
1343         initConservation();
1344         initQuality();
1345       }
1346       else
1347       {
1348         initRNAStructure();
1349       }
1350       initConsensus();
1351     }
1352   }
1353
1354   private void initConsensus()
1355   {
1356
1357     consensus = new AlignmentAnnotation("Consensus", "PID",
1358             new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
1359     consensus.hasText = true;
1360     consensus.autoCalculated = true;
1361
1362     if (showConsensus)
1363     {
1364       alignment.addAnnotation(consensus);
1365     }
1366   }
1367
1368   private void initConservation()
1369   {
1370     if (showConservation)
1371     {
1372       if (conservation == null)
1373       {
1374         conservation = new AlignmentAnnotation("Conservation",
1375                 "Conservation of total alignment less than "
1376                         + getConsPercGaps() + "% gaps", new Annotation[1],
1377                 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
1378         conservation.hasText = true;
1379         conservation.autoCalculated = true;
1380         alignment.addAnnotation(conservation);
1381       }
1382     }
1383   }
1384
1385   private void initQuality()
1386   {
1387     if (showQuality)
1388     {
1389       if (quality == null)
1390       {
1391         quality = new AlignmentAnnotation("Quality",
1392                 "Alignment Quality based on Blosum62 scores",
1393                 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
1394         quality.hasText = true;
1395         quality.autoCalculated = true;
1396         alignment.addAnnotation(quality);
1397       }
1398     }
1399   }
1400
1401   private void initRNAStructure()
1402   {
1403     if (alignment.hasRNAStructure() && strucConsensus == null)
1404     {
1405       strucConsensus = new AlignmentAnnotation("StrucConsensus", "PID",
1406               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
1407       strucConsensus.hasText = true;
1408       strucConsensus.autoCalculated = true;
1409
1410       if (showConsensus)
1411       {
1412         alignment.addAnnotation(strucConsensus);
1413       }
1414     }
1415   }
1416
1417   /*
1418    * (non-Javadoc)
1419    * 
1420    * @see jalview.api.AlignViewportI#calcPanelHeight()
1421    */
1422   @Override
1423   public int calcPanelHeight()
1424   {
1425     // setHeight of panels
1426     AlignmentAnnotation[] aa = getAlignment().getAlignmentAnnotation();
1427     int height = 0;
1428     int charHeight = getCharHeight();
1429     if (aa != null)
1430     {
1431       BitSet graphgrp = new BitSet();
1432       for (int i = 0; i < aa.length; i++)
1433       {
1434         if (aa[i] == null)
1435         {
1436           System.err.println("Null annotation row: ignoring.");
1437           continue;
1438         }
1439         if (!aa[i].visible)
1440         {
1441           continue;
1442         }
1443         if (aa[i].graphGroup > -1)
1444         {
1445           if (graphgrp.get(aa[i].graphGroup))
1446           {
1447             continue;
1448           }
1449           else
1450           {
1451             graphgrp.set(aa[i].graphGroup);
1452           }
1453         }
1454         aa[i].height = 0;
1455
1456         if (aa[i].hasText)
1457         {
1458           aa[i].height += charHeight;
1459         }
1460
1461         if (aa[i].hasIcons)
1462         {
1463           aa[i].height += 16;
1464         }
1465
1466         if (aa[i].graph > 0)
1467         {
1468           aa[i].height += aa[i].graphHeight;
1469         }
1470
1471         if (aa[i].height == 0)
1472         {
1473           aa[i].height = 20;
1474         }
1475
1476         height += aa[i].height;
1477       }
1478     }
1479     if (height == 0)
1480     {
1481       // set minimum
1482       height = 20;
1483     }
1484     return height;
1485   }
1486
1487   @Override
1488   public void updateGroupAnnotationSettings(boolean applyGlobalSettings,
1489           boolean preserveNewGroupSettings)
1490   {
1491     boolean updateCalcs = false;
1492     boolean conv = isShowGroupConservation();
1493     boolean cons = isShowGroupConsensus();
1494     boolean showprf = isShowSequenceLogo();
1495     boolean showConsHist = isShowConsensusHistogram();
1496     boolean normLogo = isNormaliseSequenceLogo();
1497
1498     /**
1499      * TODO reorder the annotation rows according to group/sequence ordering on
1500      * alignment
1501      */
1502     boolean sortg = true;
1503
1504     // remove old automatic annotation
1505     // add any new annotation
1506
1507     // intersect alignment annotation with alignment groups
1508
1509     AlignmentAnnotation[] aan = alignment.getAlignmentAnnotation();
1510     List<SequenceGroup> oldrfs = new ArrayList<SequenceGroup>();
1511     if (aan != null)
1512     {
1513       for (int an = 0; an < aan.length; an++)
1514       {
1515         if (aan[an].autoCalculated && aan[an].groupRef != null)
1516         {
1517           oldrfs.add(aan[an].groupRef);
1518           alignment.deleteAnnotation(aan[an], false);
1519         }
1520       }
1521     }
1522     if (alignment.getGroups() != null)
1523     {
1524       for (SequenceGroup sg : alignment.getGroups())
1525       {
1526         updateCalcs = false;
1527         if (applyGlobalSettings
1528                 || (!preserveNewGroupSettings && !oldrfs.contains(sg)))
1529         {
1530           // set defaults for this group's conservation/consensus
1531           sg.setshowSequenceLogo(showprf);
1532           sg.setShowConsensusHistogram(showConsHist);
1533           sg.setNormaliseSequenceLogo(normLogo);
1534         }
1535         if (conv)
1536         {
1537           updateCalcs = true;
1538           alignment.addAnnotation(sg.getConservationRow(), 0);
1539         }
1540         if (cons)
1541         {
1542           updateCalcs = true;
1543           alignment.addAnnotation(sg.getConsensus(), 0);
1544         }
1545         // refresh the annotation rows
1546         if (updateCalcs)
1547         {
1548           sg.recalcConservation();
1549         }
1550       }
1551     }
1552     oldrfs.clear();
1553   }
1554   /**
1555    * show the reference sequence in the alignment view
1556    */
1557   private boolean displayReferenceSeq=false;
1558   /**
1559    * colour according to the reference sequence defined on the alignment
1560    */
1561   private boolean colourByReferenceSeq=false;
1562
1563   @Override
1564   public boolean isDisplayReferenceSeq()
1565   {
1566     return alignment.hasSeqrep() && displayReferenceSeq;
1567   }
1568
1569   @Override
1570   public void setDisplayReferenceSeq(boolean displayReferenceSeq)
1571   {
1572     this.displayReferenceSeq = displayReferenceSeq;
1573   }
1574
1575   public boolean isColourByReferenceSeq()
1576   {
1577     return alignment.hasSeqrep() && colourByReferenceSeq;
1578   }
1579
1580   public void setColourByReferenceSeq(boolean colourByReferenceSeq)
1581   {
1582     this.colourByReferenceSeq = colourByReferenceSeq;
1583   }
1584
1585   @Override
1586   public Color getSequenceColour(SequenceI seq)
1587   {
1588     Color sqc = Color.white;
1589     if (sequenceColours != null)
1590     {
1591       sqc = (Color) sequenceColours.get(seq);
1592       if (sqc == null)
1593       {
1594         sqc = Color.white;
1595       }
1596     }
1597     return sqc;
1598   }
1599
1600   @Override
1601   public void setSequenceColour(SequenceI seq, Color col)
1602   {
1603     if (sequenceColours == null)
1604     {
1605       sequenceColours = new Hashtable();
1606     }
1607
1608     if (col == null)
1609     {
1610       sequenceColours.remove(seq);
1611     }
1612     else
1613     {
1614       sequenceColours.put(seq, col);
1615     }
1616   }
1617
1618   @Override
1619   public void updateSequenceIdColours()
1620   {
1621     if (sequenceColours == null)
1622     {
1623       sequenceColours = new Hashtable();
1624     }
1625     for (SequenceGroup sg : alignment.getGroups())
1626     {
1627       if (sg.idColour != null)
1628       {
1629         for (SequenceI s : sg.getSequences(getHiddenRepSequences()))
1630         {
1631           sequenceColours.put(s, sg.idColour);
1632         }
1633       }
1634     }
1635   }
1636
1637   @Override
1638   public void clearSequenceColours()
1639   {
1640     sequenceColours = null;
1641   };
1642
1643   FeaturesDisplayedI featuresDisplayed = null;
1644
1645   @Override
1646   public FeaturesDisplayedI getFeaturesDisplayed()
1647   {
1648     return featuresDisplayed;
1649   }
1650
1651   @Override
1652   public void setFeaturesDisplayed(FeaturesDisplayedI featuresDisplayedI)
1653   {
1654     featuresDisplayed = featuresDisplayedI;
1655   }
1656
1657   @Override
1658   public boolean areFeaturesDisplayed()
1659   {
1660     return featuresDisplayed != null && featuresDisplayed.getRegisterdFeaturesCount()>0;
1661   }
1662
1663   /**
1664    * display setting for showing/hiding sequence features on alignment view
1665    */
1666   boolean showSequenceFeatures = false;
1667
1668   /**
1669    * set the flag
1670    * 
1671    * @param b
1672    *          features are displayed if true
1673    */
1674   @Override
1675   public void setShowSequenceFeatures(boolean b)
1676   {
1677     showSequenceFeatures = b;
1678   }
1679   @Override
1680   public boolean isShowSequenceFeatures()
1681   {
1682     return showSequenceFeatures;
1683   }
1684
1685   boolean showSeqFeaturesHeight;
1686
1687   @Override
1688   public void setShowSequenceFeaturesHeight(boolean selected)
1689   {
1690     showSeqFeaturesHeight = selected;
1691   }
1692
1693   @Override
1694   public boolean isShowSequenceFeaturesHeight()
1695   {
1696     return showSeqFeaturesHeight;
1697   }
1698
1699   private boolean showAnnotation = true;
1700
1701   private boolean rightAlignIds = false;
1702
1703
1704   @Override
1705   public void setShowAnnotation(boolean b)
1706   {
1707     showAnnotation = b;
1708   }
1709
1710   @Override
1711   public boolean isShowAnnotation()
1712   {
1713     return showAnnotation;
1714   }
1715
1716   @Override
1717   public boolean isRightAlignIds()
1718   {
1719     return rightAlignIds;
1720   }
1721
1722   @Override
1723   public void setRightAlignIds(boolean rightAlignIds)
1724   {
1725     this.rightAlignIds = rightAlignIds;
1726   }
1727
1728 }