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