/*
* Jalview - A Sequence Alignment Editor and Viewer
* Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
*
* This program 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 2
* of the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package jalview.gui;
import jalview.datamodel.*;
import jalview.schemes.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
/**
* DOCUMENT ME!
*
* @author $author$
* @version $Revision$
*/
public class SeqPanel extends JPanel
{
/** DOCUMENT ME!! */
public SeqCanvas seqCanvas;
/** DOCUMENT ME!! */
public AlignmentPanel ap;
protected int lastres;
protected int startseq;
int startEdit = -1;
int endEdit = -1;
protected AlignViewport av;
// if character is inserted or deleted, we will need to recalculate the conservation
int seqEditOccurred = -1;
ScrollThread scrollThread = null;
boolean mouseDragging = false;
boolean editingSeqs = false;
boolean groupEditing = false;
//////////////////////////////////////////
/////Everything below this is for defining the boundary of the rubberband
//////////////////////////////////////////
int oldSeq = -1;
boolean changeEndSeq = false;
boolean changeStartSeq = false;
boolean changeEndRes = false;
boolean changeStartRes = false;
SequenceGroup stretchGroup = null;
boolean remove = false;
boolean mouseWheelPressed = false;
/**
* Creates a new SeqPanel object.
*
* @param avp DOCUMENT ME!
* @param p DOCUMENT ME!
*/
public SeqPanel(AlignViewport avp, AlignmentPanel p)
{
ToolTipManager.sharedInstance().registerComponent(this);
ToolTipManager.sharedInstance().setInitialDelay(0);
ToolTipManager.sharedInstance().setDismissDelay(10000);
this.av = avp;
setBackground(Color.white);
seqCanvas = new SeqCanvas(avp);
setLayout(new BorderLayout());
add(seqCanvas, BorderLayout.CENTER);
ap = p;
addMouseMotionListener(new MouseMotionAdapter()
{
public void mouseMoved(MouseEvent evt)
{
doMouseMoved(evt);
if (editingSeqs)
{
// This is because MacOSX creates a mouseMoved
// If control is down
if(!av.isDataset())
doMouseDragged(evt);
}
}
public void mouseDragged(MouseEvent evt)
{
if (editingSeqs)
{
if(!av.isDataset())
doMouseDragged(evt);
}
else
{
if(!av.isDataset())
doMouseDraggedDefineMode(evt);
}
}
});
addMouseWheelListener(new MouseWheelListener()
{
public void mouseWheelMoved(MouseWheelEvent e)
{
if (mouseWheelPressed)
{
Font font = av.getFont();
int fontSize = font.getSize();
if (e.getWheelRotation() > 0 && fontSize < 51)
fontSize++;
else if (fontSize > 1)
fontSize--;
av.setFont(new Font(font.getName(), font.getStyle(), fontSize));
ap.fontChanged();
}
else
{
if (e.getWheelRotation() > 0)
ap.scrollUp(false);
else
ap.scrollUp(true);
}
}
});
if(!av.isDataset())
{
addMouseListener(new MouseAdapter()
{
public void mouseReleased(MouseEvent evt)
{
mouseWheelPressed = false;
if (editingSeqs)
{
doMouseReleased(evt);
}
else
{
doMouseReleasedDefineMode(evt);
}
}
public void mousePressed(MouseEvent evt)
{
if (javax.swing.SwingUtilities.isMiddleMouseButton(evt))
{
mouseWheelPressed = true;
return;
}
if (evt.isShiftDown() || evt.isAltDown() ||
evt.isControlDown())
{
if (evt.isAltDown() || evt.isControlDown())
{
groupEditing = true;
}
editingSeqs = true;
doMousePressed(evt);
}
else
{
doMousePressedDefineMode(evt);
}
}
public void mouseExited(MouseEvent evt)
{
if (editingSeqs)
{
return;
}
doMouseExitedDefineMode(evt);
}
public void mouseEntered(MouseEvent evt)
{
if (editingSeqs)
{
return;
}
doMouseEnteredDefineMode(evt);
}
});
}
}
int startWrapBlock=-1;
int wrappedBlock=-1;
int findRes(MouseEvent evt)
{
int res = 0;
int x = evt.getX();
if (av.wrapAlignment)
{
int hgap = av.charHeight;
if (av.scaleAboveWrapped)
hgap += av.charHeight;
int cHeight = av.getAlignment().getHeight() * av.charHeight
+ hgap + seqCanvas.getAnnotationHeight();
int y = evt.getY();
y -= hgap;
x -= seqCanvas.LABEL_WEST;
int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());
wrappedBlock = y / cHeight;
wrappedBlock += av.getStartRes() / cwidth;
res = wrappedBlock * cwidth + x / av.getCharWidth();
}
else
{
res = (x / av.getCharWidth()) + av.getStartRes();
}
return res;
}
int findSeq(MouseEvent evt)
{
int seq = 0;
int y = evt.getY();
if (av.wrapAlignment)
{
int hgap = av.charHeight;
if (av.scaleAboveWrapped)
hgap += av.charHeight;
int cHeight = av.getAlignment().getHeight() * av.charHeight
+ hgap + seqCanvas.getAnnotationHeight();
y -= hgap;
seq = ( (y % cHeight) / av.getCharHeight());
}
else
{
seq = (y / av.getCharHeight()) + av.getStartSeq();
}
return seq;
}
/**
* DOCUMENT ME!
*
* @param evt DOCUMENT ME!
*/
public void doMouseReleased(MouseEvent evt)
{
if (seqEditOccurred > -1)
{
editOccurred(seqEditOccurred);
}
startseq = -1;
lastres = -1;
seqEditOccurred = -1;
editingSeqs = false;
groupEditing = false;
ap.repaint();
}
/**
* DOCUMENT ME!
*
* @param evt DOCUMENT ME!
*/
public void doMousePressed(MouseEvent evt)
{
ap.alignFrame.addHistoryItem(new HistoryItem("Edit Sequence",
av.alignment, HistoryItem.EDIT));
int seq = findSeq(evt);
int res = findRes(evt);
if(seq<0 || res<0)
return;
if ((seq < av.getAlignment().getHeight()) &&
(res < av.getAlignment().getSequenceAt(seq).getLength()))
{
startseq = seq;
lastres = res;
}
else
{
startseq = -1;
lastres = -1;
}
startEdit = lastres;
endEdit = lastres;
return;
}
/**
* DOCUMENT ME!
*
* @param evt DOCUMENT ME!
*/
public void doMouseMoved(MouseEvent evt)
{
int res = findRes(evt);
int seq = findSeq(evt);
if(res<0 || seq<0 || seq >= av.getAlignment().getHeight())
return;
SequenceI sequence = av.getAlignment().getSequenceAt(seq);
if (res > sequence.getLength())
{
return;
}
if(seqCanvas.pdbCanvas!=null && sequence==seqCanvas.pdbCanvas.sequence)
{
seqCanvas.pdbCanvas.highlightRes(sequence.findPosition(res));
}
StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: " +
sequence.getName());
Object obj = null;
if (av.alignment.isNucleotide())
{
obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res) +
"");
if(obj!=null)
text.append(" Nucleotide: ");
}
else
{
obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
if(obj!=null)
text.append(" Residue: ");
}
if (obj != null)
{
if (obj != "")
{
text.append( obj + " (" +
av.getAlignment().getSequenceAt(seq).findPosition(res) + ")");
}
}
ap.alignFrame.statusBar.setText(text.toString());
// use aa to see if the mouse pointer is on a
if (av.showSequenceFeatures)
{
Vector features = sequence.getDatasetSequence().getSequenceFeatures();
if(features!=null)
{
StringBuffer sbuffer = new StringBuffer("");
for (int i = 0; i < features.size(); i++)
{
SequenceFeature sf = (SequenceFeature) features.elementAt(i);
if ( (sf.getBegin() <= sequence.findPosition(res)) &&
(sf.getEnd() >= sequence.findPosition(res)))
{
if (sf.getType().equals("disulfide bond"))
{
if (sf.getBegin() == sequence.findPosition(res)
|| sf.getEnd() == sequence.findPosition(res))
{
if (sbuffer.length() > 6)
sbuffer.append("
");
sbuffer.append("disulfide bond " + sf.getBegin() + ":" +
sf.getEnd());
}
}
else
{
if (sbuffer.length() > 6)
sbuffer.append("
");
sbuffer.append(sf.getType());
if (sf.getDescription() != null)
sbuffer.append(" " + sf.getDescription());
if (sf.getStatus() != null)
{
sbuffer.append(" (" + sf.getStatus() + ")");
}
}
}
}
sbuffer.append("");
if(sbuffer.length()==13) //
setToolTipText("");
else
setToolTipText(sbuffer.toString());
}
else
setToolTipText("");
}
}
/**
* DOCUMENT ME!
*
* @param evt DOCUMENT ME!
*/
public void doMouseDragged(MouseEvent evt)
{
// If we're dragging we're editing
int res = findRes(evt);
if (res < 0)
{
res = 0;
}
if ((lastres == -1) || (lastres == res))
{
return;
}
boolean dragRight = true;
if ((res < av.getAlignment().getWidth()) && (res < lastres))
{
dragRight = false;
}
if (res != lastres)
{
// Group editing
if (groupEditing)
{
SequenceGroup sg = av.getSelectionGroup();
if (sg == null)
{
lastres = -1;
return;
}
// drag to right
if (dragRight)
{
sg.setEndRes(sg.getEndRes() + (res - lastres));
}
// drag to left
else
{
/// Are we able to delete?
// ie are all columns blank?
boolean deleteAllowed = false;
for (int s = 0; s < sg.getSize(); s++)
{
SequenceI seq = sg.getSequenceAt(s);
for (int j = res; j < lastres; j++)
{
if (seq.getSequence().length() <= j)
{
continue;
}
if (!jalview.util.Comparison.isGap(
seq.getSequence().charAt(j)))
{
// Not a gap, block edit not valid
res = j + 1;
deleteAllowed = false;
continue;
}
deleteAllowed = true;
}
}
if (!deleteAllowed)
{
lastres = -1;
return;
}
sg.setEndRes(sg.getEndRes() - (lastres - res));
}
for (int i = 0; i < sg.getSize(); i++)
{
SequenceI s = sg.getSequenceAt(i);
int k = av.alignment.findIndex(s);
// drag to right
if (dragRight)
{
for (int j = lastres; j < res; j++)
{
insertChar(j, k);
}
}
// drag to left
else
{
for (int j = res; j < lastres; j++)
{
if (s.getLength() > j)
{
deleteChar(res, k);
}
}
}
}
}
else /////Editing a single sequence///////////
{
if ((res < av.getAlignment().getWidth()) && (res > lastres))
{
// dragging to the right
for (int j = lastres; j < res; j++)
{
insertChar(j, startseq);
}
}
else if ((res < av.getAlignment().getWidth()) &&
(res < lastres))
{
// dragging to the left
for (int j = lastres; j > res; j--)
{
if (jalview.util.Comparison.isGap(
av.alignment.getSequenceAt(startseq)
.getSequence().charAt(res)))
{
deleteChar(res, startseq);
}
else
{
break;
}
}
}
}
}
endEdit = res;
lastres = res;
seqCanvas.repaint();
}
/**
* DOCUMENT ME!
*
* @param seqstart DOCUMENT ME!
* @param seqend DOCUMENT ME!
* @param start DOCUMENT ME!
*/
public void drawChars(int seqstart, int seqend, int start)
{
seqCanvas.drawPanel(seqCanvas.gg, start, av.getEndRes(), seqstart,
seqend, av.getStartRes(), av.getStartSeq(), 0);
seqCanvas.repaint();
}
/**
* DOCUMENT ME!
*
* @param j DOCUMENT ME!
* @param seq DOCUMENT ME!
*/
public void insertChar(int j, int seq)
{
av.alignment.getSequenceAt(seq).insertCharAt(j, av.getGapCharacter());
seqEditOccurred = seq;
}
/**
* DOCUMENT ME!
*
* @param j DOCUMENT ME!
* @param seq DOCUMENT ME!
*/
public void deleteChar(int j, int seq)
{
av.alignment.getSequenceAt(seq).deleteCharAt(j);
seqEditOccurred = seq;
av.alignment.getWidth();
seqCanvas.repaint();
}
/**
* DOCUMENT ME!
*
* @param i DOCUMENT ME!
*/
void editOccurred(int i)
{
if (endEdit == startEdit)
{
ap.alignFrame.historyList.pop();
ap.alignFrame.updateEditMenuBar();
}
av.firePropertyChange("alignment", null,av.getAlignment().getSequences());
}
/**
* DOCUMENT ME!
*
* @param evt DOCUMENT ME!
*/
public void doMousePressedDefineMode(MouseEvent evt)
{
int res = findRes(evt);
int seq = findSeq(evt);
oldSeq = seq;
startWrapBlock=wrappedBlock;
if(av.wrapAlignment && seq>av.alignment.getHeight())
{
JOptionPane.showInternalMessageDialog(Desktop.desktop,
"Cannot edit annotations in wrapped view.",
"Wrapped view - no edit",
JOptionPane.WARNING_MESSAGE);
return;
}
if(seq<0 || res<0)
return;
SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seq);
if ((sequence == null) || (res > sequence.getLength()))
{
return;
}
stretchGroup = av.getSelectionGroup();
if (stretchGroup == null)
{
stretchGroup = av.alignment.findGroup(sequence);
if ((stretchGroup != null) && (res > stretchGroup.getStartRes()) &&
(res < stretchGroup.getEndRes()))
{
av.setSelectionGroup(stretchGroup);
}
else
{
stretchGroup = null;
}
}
else if (!stretchGroup.sequences.contains(sequence) ||
(stretchGroup.getStartRes() > res) ||
(stretchGroup.getEndRes() < res))
{
stretchGroup = null;
SequenceGroup[] allGroups = av.alignment.findAllGroups(sequence);
if (allGroups != null)
{
for (int i = 0; i < allGroups.length; i++)
{
if ((allGroups[i].getStartRes() <= res) &&
(allGroups[i].getEndRes() >= res))
{
stretchGroup = allGroups[i];
av.setSelectionGroup(stretchGroup);
break;
}
}
}
}
if (stretchGroup == null)
{
// define a new group here
SequenceGroup sg = new SequenceGroup();
sg.setStartRes(res);
sg.setEndRes(res);
sg.addSequence(sequence, false);
av.setSelectionGroup(sg);
stretchGroup = sg;
if (av.getConservationSelected())
{
SliderPanel.setConservationSlider(ap,
av.getGlobalColourScheme(), "Background");
}
if (av.getAbovePIDThreshold())
{
SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(),
"Background");
}
}
else if (javax.swing.SwingUtilities.isRightMouseButton(evt))
{
jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu(ap, null);
pop.show(this, evt.getX(), evt.getY());
// edit the properties of existing group
}
if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))
{
// Edit end res position of selected group
changeEndRes = true;
}
else if ((stretchGroup != null) && (stretchGroup.getStartRes() == res))
{
// Edit end res position of selected group
changeStartRes = true;
}
stretchGroup.getWidth();
seqCanvas.repaint();
}
/**
* DOCUMENT ME!
*
* @param evt DOCUMENT ME!
*/
public void doMouseReleasedDefineMode(MouseEvent evt)
{
if (mouseDragging)
{
stretchGroup.recalcConservation();
mouseDragging = false;
}
if (stretchGroup == null)
{
return;
}
if(stretchGroup.cs!=null)
{
if (stretchGroup.cs instanceof ClustalxColourScheme)
{
( (ClustalxColourScheme) stretchGroup.cs).resetClustalX(stretchGroup.
sequences,
stretchGroup.getWidth());
}
if (stretchGroup.cs.conservationApplied())
{
SliderPanel.setConservationSlider(ap, stretchGroup.cs,
stretchGroup.getName());
}
else
{
SliderPanel.setPIDSliderSource(ap, stretchGroup.cs,
stretchGroup.getName());
}
}
changeEndRes = false;
changeStartRes = false;
stretchGroup = null;
PaintRefresher.Refresh(av.alignment);
}
/**
* DOCUMENT ME!
*
* @param evt DOCUMENT ME!
*/
public void doMouseDraggedDefineMode(MouseEvent evt)
{
int res = findRes(evt);
int y = findSeq(evt);
if(wrappedBlock!=startWrapBlock)
return;
if (stretchGroup == null)
{
return;
}
if (res > av.alignment.getWidth())
{
res = av.alignment.getWidth() - 1;
}
if (stretchGroup.getEndRes() == res)
{
// Edit end res position of selected group
changeEndRes = true;
}
else if (stretchGroup.getStartRes() == res)
{
// Edit start res position of selected group
changeStartRes = true;
}
if (res < av.getStartRes())
{
res = av.getStartRes();
}
else if (res > av.getEndRes() && !av.getWrapAlignment())
{
res = av.getEndRes();
}
if (changeEndRes)
{
if (res > (stretchGroup.getStartRes() - 1))
{
stretchGroup.setEndRes(res);
}
}
else if (changeStartRes)
{
if (res < (stretchGroup.getEndRes() + 1))
{
stretchGroup.setStartRes(res);
}
}
int dragDirection = 0;
if (y > oldSeq)
{
dragDirection = 1;
}
else if (y < oldSeq)
{
dragDirection = -1;
}
while ((y != oldSeq) && (oldSeq > 0) && (y < av.alignment.getHeight()))
{
// This routine ensures we don't skip any sequences, as the
// selection is quite slow.
Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);
oldSeq += dragDirection;
Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);
if (stretchGroup.sequences.contains(nextSeq))
{
stretchGroup.deleteSequence(seq, false);
}
else
{
if (seq != null)
{
stretchGroup.addSequence(seq, false);
}
stretchGroup.addSequence(nextSeq, false);
}
}
oldSeq = y;
mouseDragging = true;
if (scrollThread != null)
{
scrollThread.setEvent(evt);
}
seqCanvas.repaint();
}
/**
* DOCUMENT ME!
*
* @param e DOCUMENT ME!
*/
public void doMouseEnteredDefineMode(MouseEvent e)
{
if (scrollThread != null)
{
scrollThread.running = false;
}
}
/**
* DOCUMENT ME!
*
* @param e DOCUMENT ME!
*/
public void doMouseExitedDefineMode(MouseEvent e)
{
if (av.getWrapAlignment())
{
return;
}
if (mouseDragging)
{
scrollThread = new ScrollThread();
}
}
// this class allows scrolling off the bottom of the visible alignment
class ScrollThread extends Thread
{
MouseEvent evt;
boolean running = false;
public ScrollThread()
{
start();
}
public void setEvent(MouseEvent e)
{
evt = e;
}
public void stopScrolling()
{
running = false;
}
public void run()
{
running = true;
while (running)
{
if (evt != null)
{
if (mouseDragging && (evt.getY() < 0) &&
(av.getStartSeq() > 0))
{
running = ap.scrollUp(true);
}
if (mouseDragging && (evt.getY() >= getHeight()) &&
(av.alignment.getHeight() > av.getEndSeq()))
{
running = ap.scrollUp(false);
}
if (mouseDragging && (evt.getX() < 0))
{
running = ap.scrollRight(true);
}
else if (mouseDragging && (evt.getX() >= getWidth()))
{
running = ap.scrollRight(false);
}
}
try
{
Thread.sleep(20);
}
catch (Exception ex)
{
}
}
}
}
}