/*
* Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
* Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
* Jalview is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* Jalview is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jalview. If not, see .
* The Jalview Authors are detailed in the 'AUTHORS' file.
*/
package jalview.viewmodel;
import jalview.api.ViewStyleI;
import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.ColumnSelection;
import jalview.datamodel.SequenceCollectionI;
import jalview.datamodel.SequenceGroup;
import jalview.datamodel.SequenceI;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
/**
* Supplies and updates viewport properties relating to position such as: start
* and end residues and sequences, hidden column/row adjustments, ratio of
* viewport to alignment etc
*/
public class ViewportPositionProps extends ViewportProperties
{
// start residue of viewport
private int startRes;
// end residue of viewport
private int endRes;
// start sequence of viewport
private int startSeq;
// end sequence of viewport
private int endSeq;
// character height
private int charHeight;
// character width
private int charWidth;
// alignment
private AlignmentI al;
// viewstyle
private ViewStyleI viewstyle;
// hidden column data
protected ColumnSelection colSel = new ColumnSelection();
private long colselhash = -1;
// hidden sequence data
private Map hiddenRepSequences;
/**
* Constructor
* @param alignment TODO
*/
public ViewportPositionProps(AlignmentI alignment, ViewStyleI vstyle)
{
// initial values of viewport settings
this.startRes = 0;
this.endRes = alignment.getWidth() - 1;
this.startSeq = 0;
this.endSeq = alignment.getHeight() - 1;
this.al = alignment;
this.viewstyle = vstyle;
}
public void setCharHeight(int h)
{
viewstyle.setCharHeight(h);
}
public void setCharWidth(int w)
{
viewstyle.setCharWidth(w);
}
// ways to update values
// ways to notify of changes
// ways to supply positional information
/**
* Get alignment width
*/
public int getAlignmentWidthInCols()
{
return al.getWidth();
}
/**
* Get alignment height
*/
public int getAlignmentHeightInRows()
{
return al.getHeight();
}
public void setStartRes(int res)
{
if (res > al.getWidth() - 1)
{
res = al.getWidth() - 1;
}
else if (res < 0)
{
res = 0;
}
this.startRes = res;
}
public void setEndRes(int res)
{
if (res > al.getWidth() - 1)
{
res = al.getWidth() - 1;
}
else if (res < 0)
{
res = 0;
}
this.endRes = res;
}
public void setStartSeq(int seq)
{
if (seq > al.getHeight())
{
seq = al.getHeight();
}
else if (seq < 0)
{
seq = 0;
}
this.startSeq = seq;
}
public void setEndSeq(int seq)
{
if (seq > al.getHeight())
{
seq = al.getHeight();
}
else if (seq < 0)
{
seq = 0;
}
this.endSeq = seq;
}
/**
* Get start residue of viewport
*/
public int getStartRes()
{
return startRes;
}
/**
* Get end residue of viewport
*/
public int getEndRes()
{
return endRes;
}
/**
* Get start sequence of viewport
*/
public int getStartSeq()
{
return startSeq;
}
/**
* Get end sequence of viewport
*/
public int getEndSeq()
{
return endSeq;
}
/**
* Get start residue of viewport
*/
public int getStartRes(boolean countHidden)
{
if (countHidden)
{
return 0; // av.getColumnSelection().adjustForHiddenColumns(startRes);
}
else
{
return startRes;
}
}
/**
* Convert distance x in viewport pixels to a distance in number of residues
*
* @param x
* number of pixels
* @return number of residues
*/
public int convertPixelsToResidues(int x)
{
return Math.round((float) x / viewstyle.getCharWidth());
}
/**
* Convert distance y in viewport pixels to a distance in number of sequences
*
* @param y
* number of pixels
* @return number of sequences
*/
public int convertPixelsToSequences(int y)
{
return Math.round((float) y / viewstyle.getCharHeight());
}
/**
* Convert number of sequences s to a height in viewport pixels
*
* @param s
* number of sequences
* @return number of pixels
*/
public int convertSequencesToPixels(int s)
{
return (s * viewstyle.getCharHeight());
}
/**
* Convert number of residues r to a width in viewport pixels
*
* @param r
* number of residues
* @return number of pixels
*/
public int convertResiduesToPixels(int r)
{
return (r * viewstyle.getCharWidth());
}
public void setHiddenColumns(ColumnSelection colsel)
{
this.colSel = colsel;
}
public ColumnSelection getColumnSelection()
{
return colSel;
}
public void setColumnSelection(ColumnSelection colSel)
{
this.colSel = colSel;
if (colSel != null)
{
// updateHiddenColumns(); - does nothing
}
isColSelChanged(true);
}
public boolean hasHiddenColumns()
{
return colSel != null && colSel.hasHiddenColumns();
}
/**
* checks current colsel against record of last hash value, and optionally
* updates record.
*
* @param b
* update the record of last hash value
* @return true if colsel changed since last call (when b is true)
*/
public boolean isColSelChanged(boolean b)
{
int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel.hashCode();
if (hc != -1 && hc != colselhash)
{
if (b)
{
colselhash = hc;
}
return true;
}
return false;
}
public void hideColumns(int start, int end)
{
if (start == end)
{
colSel.hideColumns(start);
}
else
{
colSel.hideColumns(start, end);
}
isColSelChanged(true);
}
public void showColumn(int col)
{
colSel.revealHiddenColumns(col);
isColSelChanged(true);
}
public void showAllHiddenColumns()
{
colSel.revealAllHiddenColumns();
isColSelChanged(true);
}
public void invertColumnSelection()
{
colSel.invertColumnSelection(0, al.getWidth());
}
public List getVisibleRegionBoundaries(int min, int max)
{
ArrayList regions = new ArrayList();
int start = min;
int end = max;
do
{
if (hasHiddenColumns())
{
if (start == 0)
{
start = colSel.adjustForHiddenColumns(start);
}
end = colSel.getHiddenBoundaryRight(start);
if (start == end)
{
end = max;
}
if (end > max)
{
end = max;
}
}
regions.add(new int[] { start, end });
if (hasHiddenColumns())
{
start = colSel.adjustForHiddenColumns(end);
start = colSel.getHiddenBoundaryLeft(start) + 1;
}
} while (end < max);
int[][] startEnd = new int[regions.size()][2];
return regions;
}
/**
* synthesize a column selection if none exists so it covers the given
* selection group. if wholewidth is false, no column selection is made if the
* selection group covers the whole alignment width.
*
* @param sg
* @param wholewidth
*/
public void expandColSelection(SequenceGroup sg, boolean wholewidth)
{
int sgs, sge;
if (sg != null && (sgs = sg.getStartRes()) >= 0
&& sg.getStartRes() <= (sge = sg.getEndRes()))
{
if (!wholewidth && al.getWidth() == (1 + sge - sgs))
{
// do nothing
return;
}
if (colSel == null)
{
colSel = new ColumnSelection();
}
for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
{
colSel.addElement(cspos);
}
}
}
public List getVisibleAlignmentAnnotation(
boolean selectedOnly, SequenceGroup selectionGroup)
{
ArrayList ala = new ArrayList();
AlignmentAnnotation[] aa;
if ((aa = al.getAlignmentAnnotation()) != null)
{
for (AlignmentAnnotation annot : aa)
{
AlignmentAnnotation clone = new AlignmentAnnotation(annot);
if (selectedOnly && selectionGroup != null)
{
colSel.makeVisibleAnnotation(selectionGroup.getStartRes(),
selectionGroup.getEndRes(), clone);
}
else
{
colSel.makeVisibleAnnotation(clone);
}
ala.add(clone);
}
}
return ala;
}
public String[] getVisibleSequenceStrings(int start, int end,
SequenceI[] seqs)
{
return colSel.getVisibleSequenceStrings(start, end, seqs);
}
/**
* Set visibility for any annotations for the given sequence.
*
* @param sequenceI
*/
protected void setSequenceAnnotationsVisible(SequenceI sequenceI,
boolean visible)
{
AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
if (anns != null)
{
for (AlignmentAnnotation ann : anns)
{
if (ann.sequenceRef == sequenceI)
{
ann.visible = visible;
}
}
}
}
public void setHiddenRepSequences(
Map hiddenRepSequences)
{
this.hiddenRepSequences = hiddenRepSequences;
}
public Map getHiddenRepSequences()
{
return hiddenRepSequences;
}
// common hide/show seq stuff
public SequenceGroup showAllHiddenSeqs(SequenceGroup selectionGroup)
{
if (al.getHiddenSequences().getSize() > 0)
{
if (selectionGroup == null)
{
selectionGroup = new SequenceGroup();
selectionGroup.setEndRes(al.getWidth() - 1);
}
List tmp = al.getHiddenSequences().showAll(
hiddenRepSequences);
for (SequenceI seq : tmp)
{
selectionGroup.addSequence(seq, false);
setSequenceAnnotationsVisible(seq, true);
}
hiddenRepSequences = null;
firePropertyChange("alignment", null, al.getSequences());
// used to set hasHiddenRows/hiddenRepSequences here, after the property
// changed event
sendSelection();
}
}
public void showSequence(int index, SequenceGroup selectionGroup)
{
List tmp = al.getHiddenSequences().showSequence(index,
hiddenRepSequences);
if (tmp.size() > 0)
{
if (selectionGroup == null)
{
selectionGroup = new SequenceGroup();
selectionGroup.setEndRes(al.getWidth() - 1);
}
for (SequenceI seq : tmp)
{
selectionGroup.addSequence(seq, false);
setSequenceAnnotationsVisible(seq, true);
}
firePropertyChange("alignment", null, al.getSequences());
sendSelection();
}
}
public void hideAllSelectedSeqs(SequenceGroup selectionGroup)
{
if (selectionGroup == null || selectionGroup.getSize() < 1)
{
return;
}
SequenceI[] seqs = selectionGroup.getSequencesInOrder(al);
hideSequence(seqs);
setSelectionGroup(null);
}
public void hideSequence(SequenceI[] seq)
{
if (seq != null)
{
for (int i = 0; i < seq.length; i++)
{
al.getHiddenSequences().hideSequence(seq[i]);
setSequenceAnnotationsVisible(seq[i], false);
}
firePropertyChange("alignment", null, al.getSequences());
}
}
/**
* Hides the specified sequence, or the sequences it represents
*
* @param sequence
* the sequence to hide, or keep as representative
* @param representGroup
* if true, hide the current selection group except for the
* representative sequence
*/
public void hideSequences(SequenceI sequence, boolean representGroup,
SequenceGroup selectionGroup)
{
if (selectionGroup == null || selectionGroup.getSize() < 1)
{
hideSequence(new SequenceI[] { sequence });
return;
}
if (representGroup)
{
hideRepSequences(sequence, selectionGroup);
setSelectionGroup(null);
return;
}
int gsize = selectionGroup.getSize();
SequenceI[] hseqs = selectionGroup.getSequences().toArray(
new SequenceI[gsize]);
hideSequence(hseqs);
setSelectionGroup(null);
sendSelection();
}
public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
{
int sSize = sg.getSize();
if (sSize < 2)
{
return;
}
if (hiddenRepSequences == null)
{
hiddenRepSequences = new Hashtable();
}
hiddenRepSequences.put(repSequence, sg);
// Hide all sequences except the repSequence
SequenceI[] seqs = new SequenceI[sSize - 1];
int index = 0;
for (int i = 0; i < sSize; i++)
{
if (sg.getSequenceAt(i) != repSequence)
{
if (index == sSize - 1)
{
return;
}
seqs[index++] = sg.getSequenceAt(i);
}
}
sg.setSeqrep(repSequence); // note: not done in 2.7applet
sg.setHidereps(true); // note: not done in 2.7applet
hideSequence(seqs);
}
/**
*
* @param seq
* @return true if there are sequences represented by this sequence that are
* currently hidden
*/
public boolean isHiddenRepSequence(SequenceI seq)
{
return (hiddenRepSequences != null && hiddenRepSequences
.containsKey(seq));
}
/**
*
* @param seq
* @return null or a sequence group containing the sequences that seq
* represents
*/
public SequenceGroup getRepresentedSequences(SequenceI seq)
{
return (SequenceGroup) (hiddenRepSequences == null ? null
: hiddenRepSequences.get(seq));
}
public int adjustForHiddenSeqs(int alignmentIndex)
{
return al.getHiddenSequences().adjustForHiddenSeqs(alignmentIndex);
}
}