2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3 * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
11 * Jalview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
18 package jalview.viewmodel;
20 import jalview.analysis.Conservation;
21 import jalview.api.AlignCalcManagerI;
22 import jalview.api.AlignViewportI;
23 import jalview.api.AlignmentViewPanel;
24 import jalview.datamodel.AlignmentAnnotation;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.AlignmentView;
27 import jalview.datamodel.Annotation;
28 import jalview.datamodel.ColumnSelection;
29 import jalview.datamodel.Sequence;
30 import jalview.datamodel.SequenceGroup;
31 import jalview.datamodel.SequenceI;
32 import jalview.schemes.ClustalxColourScheme;
33 import jalview.schemes.ColourSchemeI;
34 import jalview.schemes.ResidueProperties;
35 import jalview.workers.AlignCalcManager;
36 import jalview.workers.ConsensusThread;
37 import jalview.workers.ConservationThread;
38 import jalview.workers.StrucConsensusThread;
40 import java.util.Hashtable;
41 import java.util.Vector;
44 * base class holding visualization and analysis attributes and common logic for an active alignment view displayed in the GUI
48 public abstract class AlignmentViewport implements AlignViewportI
51 * alignment displayed in the viewport. Please use get/setter
53 protected AlignmentI alignment;
55 protected String sequenceSetID;
58 * probably unused indicator that view is of a dataset rather than an alignment
60 protected boolean isDataset = false;
62 private Hashtable hiddenRepSequences;
64 protected ColumnSelection colSel = new ColumnSelection();
67 public boolean autoCalculateConsensus = true;
69 protected boolean autoCalculateStrucConsensus = true;
71 protected boolean ignoreGapsInConsensusCalculation = false;
74 protected ColourSchemeI globalColourScheme = null;
77 public void setGlobalColourScheme(ColourSchemeI cs)
79 globalColourScheme = cs;
82 public ColourSchemeI getGlobalColourScheme()
84 return globalColourScheme;
88 protected AlignmentAnnotation consensus;
90 protected AlignmentAnnotation strucConsensus;
92 protected AlignmentAnnotation conservation;
94 protected AlignmentAnnotation quality;
96 protected AlignmentAnnotation[] groupConsensus;
98 protected AlignmentAnnotation[] groupConservation;
101 * results of alignment consensus analysis for visible portion of view
103 protected Hashtable[] hconsensus=null;
106 * results of secondary structure base pair consensus for visible portion of view
108 protected Hashtable[] hStrucConsensus=null;
111 * percentage gaps allowed in a column before all amino acid properties should be considered unconserved
113 int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
116 public int getConsPercGaps()
121 public void setSequenceConsensusHash(Hashtable[] hconsensus)
123 this.hconsensus=hconsensus;
128 public Hashtable[] getSequenceConsensusHash()
134 public Hashtable[] getRnaStructureConsensusHash()
136 return hStrucConsensus;
139 public void setRnaStructureConsensusHash(Hashtable[] hStrucConsensus)
141 this.hStrucConsensus=hStrucConsensus;
145 public AlignmentAnnotation getAlignmentQualityAnnot()
151 public AlignmentAnnotation getAlignmentConservationAnnotation()
156 public AlignmentAnnotation getAlignmentConsensusAnnotation()
161 public AlignmentAnnotation getAlignmentStrucConsensusAnnotation()
163 return strucConsensus;
166 protected AlignCalcManagerI calculator=new AlignCalcManager();
169 * trigger update of conservation annotation
171 public void updateConservation(final AlignmentViewPanel ap)
173 // see note in mantis : issue number 8585
174 if (alignment.isNucleotide() || conservation == null
175 || !autoCalculateConsensus)
179 if (!calculator.startRegisteredWorkersOfClass(jalview.workers.ConservationThread.class))
181 calculator.registerWorker(new jalview.workers.ConservationThread(this, ap));
186 * trigger update of consensus annotation
188 public void updateConsensus(final AlignmentViewPanel ap)
190 // see note in mantis : issue number 8585
191 if (consensus == null || !autoCalculateConsensus)
195 if (!calculator.startRegisteredWorkersOfClass(ConsensusThread.class))
197 calculator.registerWorker(new ConsensusThread(this, ap));
201 // --------START Structure Conservation
202 public void updateStrucConsensus(final AlignmentViewPanel ap)
204 if (autoCalculateStrucConsensus && strucConsensus==null && alignment.isNucleotide() && alignment.hasRNAStructure())
209 // see note in mantis : issue number 8585
210 if (strucConsensus == null || !autoCalculateStrucConsensus)
214 if (!calculator.startRegisteredWorkersOfClass(StrucConsensusThread.class))
216 calculator.registerWorker(new StrucConsensusThread(this,ap));
220 public boolean isCalcInProgress()
222 return calculator.isWorking();
225 public boolean isCalculationInProgress(
226 AlignmentAnnotation alignmentAnnotation)
228 if (!alignmentAnnotation.autoCalculated)
230 if (calculator.workingInvolvedWith(alignmentAnnotation))
232 // System.err.println("grey out ("+alignmentAnnotation.label+")");
238 public boolean isClosed()
240 // TODO: check that this isClosed is only true after panel is closed, not before it is fully constructed.
241 return alignment==null;
245 public AlignCalcManagerI getCalcManager()
251 * should conservation rows be shown for groups
253 protected boolean showGroupConservation = false;
256 * should consensus rows be shown for groups
258 protected boolean showGroupConsensus = false;
261 * should consensus profile be rendered by default
263 protected boolean showSequenceLogo = false;
265 * should consensus profile be rendered normalised to row height
267 protected boolean normaliseSequenceLogo = false;
269 * should consensus histograms be rendered by default
271 protected boolean showConsensusHistogram = true;
274 * @return the showConsensusProfile
276 public boolean isShowSequenceLogo()
278 return showSequenceLogo;
282 * @param showSequenceLogo
285 public void setShowSequenceLogo(boolean showSequenceLogo)
287 if (showSequenceLogo != this.showSequenceLogo)
289 // TODO: decouple settings setting from calculation when refactoring
290 // annotation update method from alignframe to viewport
291 this.showSequenceLogo = showSequenceLogo;
292 calculator.updateAnnotationFor(ConsensusThread.class);
293 calculator.updateAnnotationFor(StrucConsensusThread.class);
295 this.showSequenceLogo = showSequenceLogo;
299 * @param showConsensusHistogram
300 * the showConsensusHistogram to set
302 public void setShowConsensusHistogram(boolean showConsensusHistogram)
304 this.showConsensusHistogram = showConsensusHistogram;
308 * @return the showGroupConservation
310 public boolean isShowGroupConservation()
312 return showGroupConservation;
316 * @param showGroupConservation
317 * the showGroupConservation to set
319 public void setShowGroupConservation(boolean showGroupConservation)
321 this.showGroupConservation = showGroupConservation;
325 * @return the showGroupConsensus
327 public boolean isShowGroupConsensus()
329 return showGroupConsensus;
333 * @param showGroupConsensus
334 * the showGroupConsensus to set
336 public void setShowGroupConsensus(boolean showGroupConsensus)
338 this.showGroupConsensus = showGroupConsensus;
343 * @return flag to indicate if the consensus histogram should be rendered by
346 public boolean isShowConsensusHistogram()
348 return this.showConsensusHistogram;
352 * show non-conserved residues only
354 protected boolean showUnconserved = false;
358 * when set, updateAlignment will always ensure sequences are of equal length
360 private boolean padGaps = false;
363 * when set, alignment should be reordered according to a newly opened tree
365 public boolean sortByTree = false;
367 public boolean getShowUnconserved()
369 return showUnconserved;
372 public void setShowUnconserved(boolean showunconserved)
374 showUnconserved = showunconserved;
378 * @param showNonconserved
379 * the showUnconserved to set
381 public void setShowunconserved(boolean displayNonconserved)
383 this.showUnconserved = displayNonconserved;
389 * @return null or the currently selected sequence region
391 public SequenceGroup getSelectionGroup()
393 return selectionGroup;
397 * Set the selection group for this window.
400 * - group holding references to sequences in this alignment view
403 public void setSelectionGroup(SequenceGroup sg)
408 public void setHiddenColumns(ColumnSelection colsel)
410 this.colSel = colsel;
411 if (colSel.getHiddenColumns() != null)
413 hasHiddenColumns = true;
417 public ColumnSelection getColumnSelection()
421 public void setColumnSelection(ColumnSelection colSel)
425 public Hashtable getHiddenRepSequences()
427 return hiddenRepSequences;
429 public void setHiddenRepSequences(Hashtable hiddenRepSequences)
431 this.hiddenRepSequences = hiddenRepSequences;
433 protected boolean hasHiddenColumns = false;
435 public void updateHiddenColumns()
437 hasHiddenColumns = colSel.getHiddenColumns() != null;
440 protected boolean hasHiddenRows = false;
442 public boolean hasHiddenRows() {
443 return hasHiddenRows;
446 protected SequenceGroup selectionGroup;
448 public void setSequenceSetId(String newid)
450 if (sequenceSetID!=null)
452 System.err.println("Warning - overwriting a sequenceSetId for a viewport!");
454 sequenceSetID=new String(newid);
456 public String getSequenceSetId()
458 if (sequenceSetID == null)
460 sequenceSetID = alignment.hashCode() + "";
463 return sequenceSetID;
466 * unique viewId for synchronizing state (e.g. with stored Jalview Project)
469 protected String viewId = null;
471 public String getViewId()
475 viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
479 public void setIgnoreGapsConsensus(boolean b, AlignmentViewPanel ap)
481 ignoreGapsInConsensusCalculation = b;
482 if (ap!=null) {updateConsensus(ap);
483 if (globalColourScheme != null)
485 globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
486 ignoreGapsInConsensusCalculation);
490 private long sgrouphash = -1, colselhash = -1;
493 * checks current SelectionGroup against record of last hash value, and
497 * update the record of last hash value
499 * @return true if SelectionGroup changed since last call (when b is true)
501 public boolean isSelectionGroupChanged(boolean b)
503 int hc = (selectionGroup == null || selectionGroup.getSize() == 0) ? -1
504 : selectionGroup.hashCode();
505 if (hc != -1 && hc != sgrouphash)
517 * checks current colsel against record of last hash value, and optionally
521 * update the record of last hash value
522 * @return true if colsel changed since last call (when b is true)
524 public boolean isColSelChanged(boolean b)
526 int hc = (colSel == null || colSel.size() == 0) ? -1 : colSel
528 if (hc != -1 && hc != colselhash)
539 public boolean getIgnoreGapsConsensus()
541 return ignoreGapsInConsensusCalculation;
544 /// property change stuff
546 // JBPNote Prolly only need this in the applet version.
547 private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
550 protected boolean showConservation = true;
552 protected boolean showQuality = true;
554 protected boolean showConsensus = true;
558 * Property change listener for changes in alignment
563 public void addPropertyChangeListener(
564 java.beans.PropertyChangeListener listener)
566 changeSupport.addPropertyChangeListener(listener);
575 public void removePropertyChangeListener(
576 java.beans.PropertyChangeListener listener)
578 changeSupport.removePropertyChangeListener(listener);
582 * Property change listener for changes in alignment
591 public void firePropertyChange(String prop, Object oldvalue,
594 changeSupport.firePropertyChange(prop, oldvalue, newvalue);
597 // common hide/show column stuff
600 public void hideSelectedColumns()
602 if (colSel.size() < 1)
607 colSel.hideSelectedColumns();
608 setSelectionGroup(null);
610 hasHiddenColumns = true;
613 public void hideColumns(int start, int end)
617 colSel.hideColumns(start);
621 colSel.hideColumns(start, end);
624 hasHiddenColumns = true;
627 public void showColumn(int col)
629 colSel.revealHiddenColumns(col);
630 if (colSel.getHiddenColumns() == null)
632 hasHiddenColumns = false;
636 public void showAllHiddenColumns()
638 colSel.revealAllHiddenColumns();
639 hasHiddenColumns = false;
643 // common hide/show seq stuff
644 public void showAllHiddenSeqs()
646 if (alignment.getHiddenSequences().getSize() > 0)
648 if (selectionGroup == null)
650 selectionGroup = new SequenceGroup();
651 selectionGroup.setEndRes(alignment.getWidth() - 1);
653 Vector tmp = alignment.getHiddenSequences().showAll(
655 for (int t = 0; t < tmp.size(); t++)
657 selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
660 hasHiddenRows = false;
661 hiddenRepSequences = null;
663 firePropertyChange("alignment", null, alignment.getSequences());
664 // used to set hasHiddenRows/hiddenRepSequences here, after the property changed event
669 public void showSequence(int index)
671 Vector tmp = alignment.getHiddenSequences().showSequence(index,
675 if (selectionGroup == null)
677 selectionGroup = new SequenceGroup();
678 selectionGroup.setEndRes(alignment.getWidth() - 1);
681 for (int t = 0; t < tmp.size(); t++)
683 selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
685 // JBPNote: refactor: only update flag if we modified visiblity (used to do this regardless)
686 if (alignment.getHiddenSequences().getSize() < 1)
688 hasHiddenRows = false;
690 firePropertyChange("alignment", null, alignment.getSequences());
697 public void hideAllSelectedSeqs()
699 if (selectionGroup == null || selectionGroup.getSize() < 1)
704 SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
708 setSelectionGroup(null);
712 public void hideSequence(SequenceI[] seq)
716 for (int i = 0; i < seq.length; i++)
718 alignment.getHiddenSequences().hideSequence(seq[i]);
720 hasHiddenRows = true;
721 firePropertyChange("alignment", null, alignment.getSequences());
725 public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
727 int sSize = sg.getSize();
733 if (hiddenRepSequences == null)
735 hiddenRepSequences = new Hashtable();
738 hiddenRepSequences.put(repSequence, sg);
740 // Hide all sequences except the repSequence
741 SequenceI[] seqs = new SequenceI[sSize - 1];
743 for (int i = 0; i < sSize; i++)
745 if (sg.getSequenceAt(i) != repSequence)
747 if (index == sSize - 1)
752 seqs[index++] = sg.getSequenceAt(i);
755 sg.setSeqrep(repSequence); // note: not done in 2.7applet
756 sg.setHidereps(true); // note: not done in 2.7applet
761 public boolean isHiddenRepSequence(SequenceI seq)
763 return hiddenRepSequences != null
764 && hiddenRepSequences.containsKey(seq);
766 public SequenceGroup getRepresentedSequences(SequenceI seq)
768 return (SequenceGroup) (hiddenRepSequences == null ? null : hiddenRepSequences.get(seq));
771 public int adjustForHiddenSeqs(int alignmentIndex)
773 return alignment.getHiddenSequences().adjustForHiddenSeqs(
777 // Selection manipulation
779 * broadcast selection to any interested parties
781 public abstract void sendSelection();
784 public void invertColumnSelection()
786 colSel.invertColumnSelection(0, alignment.getWidth());
791 * This method returns an array of new SequenceI objects derived from the
792 * whole alignment or just the current selection with start and end points
795 * @note if you need references to the actual SequenceI objects in the
796 * alignment or currently selected then use getSequenceSelection()
797 * @return selection as new sequenceI objects
799 public SequenceI[] getSelectionAsNewSequence()
801 SequenceI[] sequences;
802 // JBPNote: Need to test jalviewLite.getSelectedSequencesAsAlignmentFrom - this was the only caller in the applet for this method
803 // JBPNote: in applet, this method returned references to the alignment sequences, and it did not honour the presence/absence of annotation attached to the alignment (probably!)
804 if (selectionGroup == null)
806 sequences = alignment.getSequencesArray();
807 AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
808 for (int i = 0; i < sequences.length; i++)
810 sequences[i] = new Sequence(sequences[i], annots); // construct new
818 sequences = selectionGroup.getSelectionAsNewSequences(alignment);
826 * get the currently selected sequence objects or all the sequences in the
829 * @return array of references to sequence objects
831 public SequenceI[] getSequenceSelection()
833 SequenceI[] sequences = null;
834 if (selectionGroup != null)
836 sequences = selectionGroup.getSequencesInOrder(alignment);
838 if (sequences == null)
840 sequences = alignment.getSequencesArray();
847 * This method returns the visible alignment as text, as seen on the GUI, ie
848 * if columns are hidden they will not be returned in the result. Use this for
849 * calculating trees, PCA, redundancy etc on views which contain hidden
854 public jalview.datamodel.CigarArray getViewAsCigars(
855 boolean selectedRegionOnly)
857 return new jalview.datamodel.CigarArray(alignment,
858 (hasHiddenColumns ? colSel : null),
859 (selectedRegionOnly ? selectionGroup : null));
863 * return a compact representation of the current alignment selection to pass
864 * to an analysis function
866 * @param selectedOnly
867 * boolean true to just return the selected view
868 * @return AlignmentView
870 public jalview.datamodel.AlignmentView getAlignmentView(
871 boolean selectedOnly)
873 return getAlignmentView(selectedOnly, false);
877 * return a compact representation of the current alignment selection to pass
878 * to an analysis function
880 * @param selectedOnly
881 * boolean true to just return the selected view
883 * boolean true to annotate the alignment view with groups on the
884 * alignment (and intersecting with selected region if selectedOnly
886 * @return AlignmentView
888 public jalview.datamodel.AlignmentView getAlignmentView(
889 boolean selectedOnly, boolean markGroups)
891 return new AlignmentView(alignment, colSel, selectionGroup,
892 hasHiddenColumns, selectedOnly, markGroups);
897 * This method returns the visible alignment as text, as seen on the GUI, ie
898 * if columns are hidden they will not be returned in the result. Use this for
899 * calculating trees, PCA, redundancy etc on views which contain hidden
904 public String[] getViewAsString(boolean selectedRegionOnly)
906 String[] selection = null;
907 SequenceI[] seqs = null;
909 int start = 0, end = 0;
910 if (selectedRegionOnly && selectionGroup != null)
912 iSize = selectionGroup.getSize();
913 seqs = selectionGroup.getSequencesInOrder(alignment);
914 start = selectionGroup.getStartRes();
915 end = selectionGroup.getEndRes() + 1;
919 iSize = alignment.getHeight();
920 seqs = alignment.getSequencesArray();
921 end = alignment.getWidth();
924 selection = new String[iSize];
925 if (hasHiddenColumns)
927 selection = colSel.getVisibleSequenceStrings(start, end, seqs);
931 for (i = 0; i < iSize; i++)
933 selection[i] = seqs[i].getSequenceAsString(start, end);
942 * return visible region boundaries within given column range
943 * @param min first column (inclusive, from 0)
944 * @param max last column (exclusive)
945 * @return int[][] range of {start,end} visible positions
947 public int[][] getVisibleRegionBoundaries(int min, int max)
949 Vector regions = new Vector();
955 if (hasHiddenColumns)
959 start = colSel.adjustForHiddenColumns(start);
962 end = colSel.getHiddenBoundaryRight(start);
973 regions.addElement(new int[]
976 if (hasHiddenColumns)
978 start = colSel.adjustForHiddenColumns(end);
979 start = colSel.getHiddenBoundaryLeft(start) + 1;
983 int[][] startEnd = new int[regions.size()][2];
985 regions.copyInto(startEnd);
991 * @return the padGaps
993 public boolean isPadGaps()
998 * @param padGaps the padGaps to set
1000 public void setPadGaps(boolean padGaps)
1002 this.padGaps = padGaps;
1005 * apply any post-edit constraints and trigger any calculations needed after an edit has been performed on the alignment
1008 public void alignmentChanged(AlignmentViewPanel ap)
1012 alignment.padGaps();
1014 if (autoCalculateConsensus)
1016 updateConsensus(ap);
1018 if (hconsensus != null && autoCalculateConsensus)
1020 updateConservation(ap);
1022 if (autoCalculateStrucConsensus)
1024 updateStrucConsensus(ap);
1027 // Reset endRes of groups if beyond alignment width
1028 int alWidth = alignment.getWidth();
1029 Vector groups = alignment.getGroups();
1032 for (int i = 0; i < groups.size(); i++)
1034 SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
1035 if (sg.getEndRes() > alWidth)
1037 sg.setEndRes(alWidth - 1);
1042 if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1044 selectionGroup.setEndRes(alWidth - 1);
1047 resetAllColourSchemes();
1048 calculator.restartWorkers();
1049 // alignment.adjustSequenceAnnotations();
1054 * reset scope and do calculations for all applied colourschemes on alignment
1056 void resetAllColourSchemes()
1058 ColourSchemeI cs = globalColourScheme;
1061 if (cs instanceof ClustalxColourScheme)
1063 ((ClustalxColourScheme) cs).resetClustalX(alignment.getSequences(),
1064 alignment.getWidth());
1067 cs.setConsensus(hconsensus);
1068 if (cs.conservationApplied())
1070 cs.setConservation(Conservation.calculateConservation("All",
1071 ResidueProperties.propHash, 3, alignment.getSequences(), 0,
1072 alignment.getWidth(), false, getConsPercGaps(), false));
1076 int s, sSize = alignment.getGroups().size();
1077 for (s = 0; s < sSize; s++)
1079 SequenceGroup sg = (SequenceGroup) alignment.getGroups().elementAt(s);
1080 if (sg.cs != null && sg.cs instanceof ClustalxColourScheme)
1082 ((ClustalxColourScheme) sg.cs).resetClustalX(sg
1083 .getSequences(hiddenRepSequences), sg.getWidth());
1085 sg.recalcConservation();
1089 protected void initAutoAnnotation()
1091 // TODO: add menu option action that nulls or creates consensus object
1092 // depending on if the user wants to see the annotation or not in a
1093 // specific alignment
1095 if (hconsensus == null && !isDataset)
1097 if (!alignment.isNucleotide())
1099 if (showConservation)
1101 if (conservation==null)
1103 conservation = new AlignmentAnnotation("Conservation",
1104 "Conservation of total alignment less than " + getConsPercGaps()
1105 + "% gaps", new Annotation[1], 0f, 11f,
1106 AlignmentAnnotation.BAR_GRAPH);
1107 conservation.hasText = true;
1108 conservation.autoCalculated = true;
1109 alignment.addAnnotation(conservation);
1116 quality = new AlignmentAnnotation("Quality",
1117 "Alignment Quality based on Blosum62 scores",
1118 new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
1119 quality.hasText = true;
1120 quality.autoCalculated = true;
1121 alignment.addAnnotation(quality);
1125 if (alignment.hasRNAStructure())
1127 strucConsensus = new AlignmentAnnotation("StrucConsensus", "PID",
1128 new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
1129 strucConsensus.hasText = true;
1130 strucConsensus.autoCalculated = true;
1134 consensus = new AlignmentAnnotation("Consensus", "PID",
1135 new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
1136 consensus.hasText = true;
1137 consensus.autoCalculated = true;
1141 alignment.addAnnotation(consensus);
1142 if (strucConsensus!=null)
1144 alignment.addAnnotation(strucConsensus);