/* * 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 javax.swing.*; /** * DOCUMENT ME! * * @author $author$ * @version $Revision$ */ public class SeqPanel extends JPanel implements MouseListener, MouseMotionListener, MouseWheelListener { /** DOCUMENT ME!! */ public SeqCanvas seqCanvas; /** DOCUMENT ME!! */ public AlignmentPanel ap; protected int lastres; protected int startseq; protected AlignViewport av; // if character is inserted or deleted, we will need to recalculate the conservation boolean seqEditOccurred = false; 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; if(!av.isDataset()) { addMouseMotionListener(this); addMouseListener(this); addMouseWheelListener(this); } } 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(); } if(av.hasHiddenColumns) res = av.getColumnSelection().adjustForHiddenColumns(res); 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; } void endEditing() { startseq = -1; lastres = -1; seqEditOccurred = false; editingSeqs = false; groupEditing = false; keyboardGaps = null; } void moveCursor(int dx, int dy) { seqCanvas.cursorX += dx; seqCanvas.cursorY += dy; if(seqCanvas.cursorX<0) seqCanvas.cursorX = 0; else if(seqCanvas.cursorX>av.alignment.getWidth()-1) seqCanvas.cursorX = av.alignment.getWidth()-1; if(seqCanvas.cursorXav.endRes) ap.scrollRight(true); if(seqCanvas.cursorY<0) seqCanvas.cursorY=0; else if(seqCanvas.cursorY>av.alignment.getHeight()-1) seqCanvas.cursorY = av.alignment.getHeight()-1; if(seqCanvas.cursorYav.endSeq) ap.scrollUp(false); setStatusMessage(av.alignment.getSequenceAt(seqCanvas.cursorY), seqCanvas.cursorX, seqCanvas.cursorY); seqCanvas.repaint(); } void setSelectionAreaAtCursor(boolean topLeft) { SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seqCanvas.cursorY); if(av.getSelectionGroup()!=null) { SequenceGroup sg = av.selectionGroup; //Find the top and bottom of this group int min = av.alignment.getHeight(), max = 0; for(int i=0; i max) max = index; if(index < min) min = index; } max ++; if(topLeft) { sg.setStartRes(seqCanvas.cursorX); if(sg.getEndRes()seqCanvas.cursorX) sg.setStartRes(seqCanvas.cursorX); max = seqCanvas.cursorY+1; } if(min>max) { // Only the user can do this av.setSelectionGroup(null); } else { // Now add any sequences between min and max sg.sequences.clear(); for (int i = min; i < max; i++) { sg.addSequence(av.alignment.getSequenceAt(i), false); } } } if (av.getSelectionGroup() == null) { SequenceGroup sg = new SequenceGroup(); sg.setStartRes(seqCanvas.cursorX); sg.setEndRes(seqCanvas.cursorX); sg.addSequence(sequence, false); av.setSelectionGroup(sg); } ap.repaint(); } void insertGapAtCursor(boolean group) { ap.alignFrame.addHistoryItem(new HistoryItem("Edit Sequence", av.alignment, HistoryItem.EDIT)); groupEditing = group; startseq = seqCanvas.cursorY; lastres = seqCanvas.cursorX; editSequence(true, seqCanvas.cursorX+getKeyboardGaps()); editOccurred(); } void deleteGapAtCursor(boolean group) { ap.alignFrame.addHistoryItem(new HistoryItem("Edit Sequence", av.alignment, HistoryItem.EDIT)); groupEditing = group; startseq = seqCanvas.cursorY; lastres = seqCanvas.cursorX+getKeyboardGaps(); editSequence(false, seqCanvas.cursorX); editOccurred(); } void numberPressed(char value) { if(keyboardGaps==null) keyboardGaps = new StringBuffer(); keyboardGaps.append(value); } StringBuffer keyboardGaps; int getKeyboardGaps() { if(keyboardGaps==null) return 1; else return Integer.parseInt(keyboardGaps.toString()); } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void mouseReleased(MouseEvent evt) { mouseDragging = false; mouseWheelPressed = false; if (!editingSeqs) { doMouseReleasedDefineMode(evt); return; } editOccurred(); endEditing(); ap.repaint(); } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ 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; } else { doMousePressedDefineMode(evt); return; } int seq = findSeq(evt); int res = findRes(evt); if(seq<0 || res<0) return; ap.alignFrame.addHistoryItem(new HistoryItem("Edit Sequence", av.alignment, HistoryItem.EDIT)); if ((seq < av.getAlignment().getHeight()) && (res < av.getAlignment().getSequenceAt(seq).getLength())) { startseq = seq; lastres = res; } else { startseq = -1; lastres = -1; } return; } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void mouseMoved(MouseEvent evt) { if (editingSeqs) { // This is because MacOSX creates a mouseMoved // If control is down, other platforms will not. mouseDragged(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)); } setStatusMessage(sequence, res, seq); // use aa to see if the mouse pointer is on a if (av.showSequenceFeatures) { SequenceFeature [] features = sequence.getDatasetSequence().getSequenceFeatures(); if(features!=null) { StringBuffer sbuffer = new StringBuffer(""); for (int i = 0; i < features.length; i++) { if ( (features[i].getBegin() <= sequence.findPosition(res)) && (features[i].getEnd() >= sequence.findPosition(res))) { if(av.featuresDisplayed==null || !av.featuresDisplayed.containsKey(features[i].getType())) continue; if (features[i].getType().equals("disulfide bond")) { if (features[i].getBegin() == sequence.findPosition(res) || features[i].getEnd() == sequence.findPosition(res)) { if (sbuffer.length() > 6) sbuffer.append("
"); sbuffer.append("disulfide bond " + features[i].getBegin() + ":" + features[i].getEnd()); } } else { if (sbuffer.length() > 6) sbuffer.append("
"); if(features[i].featureGroup!=null) sbuffer.append(features[i].featureGroup+";"); sbuffer.append(features[i].getType()); if (features[i].getDescription() != null && !features[i].description.equals(features[i].getType())) sbuffer.append("; " + features[i].getDescription()); if (features[i].getStatus() != null && features[i].getStatus().length()>0) { sbuffer.append("; (" + features[i].getStatus() + ")"); } } } } sbuffer.append(""); if(sbuffer.length()==13) // setToolTipText(""); else setToolTipText(sbuffer.toString()); } else setToolTipText(""); } } void setStatusMessage(SequenceI sequence, int res, int seq) { 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 + " (" + sequence.findPosition(res) + ")"); } } ap.alignFrame.statusBar.setText(text.toString()); } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void mouseDragged(MouseEvent evt) { if (!editingSeqs) { doMouseDraggedDefineMode(evt); return; } int res = findRes(evt); if (res < 0) { res = 0; } if ((lastres == -1) || (lastres == res)) { return; } if ( (res < av.getAlignment().getWidth()) && (res < lastres)) { // dragLeft, delete gap editSequence(false, res); } else editSequence(true, res); mouseDragging = true; if(scrollThread!=null) scrollThread.setEvent(evt); } void editSequence(boolean insertGap, int startres) { int fixedLeft = -1; int fixedRight = -1; boolean fixedColumns = false; if(insertGap && av.hasHiddenColumns) { //Stop editing if the user has dragged beyond hiddenBoundary fixedRight = av.getColumnSelection().getHiddenRegionBoundary(lastres); if( fixedRight < lastres) { if(lastres!=fixedRight) { endEditing(); return; } } } if (!groupEditing && av.hasHiddenRows) { if (av.alignment.getSequenceAt(startseq).getHiddenSequences() != null) { groupEditing = true; } } SequenceI seq = av.alignment.getSequenceAt(startseq); StringBuffer message = new StringBuffer(); if (groupEditing) message.append("Edit group:"); else message.append("Edit sequence: "+seq.getName()); if(insertGap) message.append(" insert "); else message.append(" delete "); message.append(Math.abs(startres-lastres)+" gaps."); ap.alignFrame.statusBar.setText(message.toString()); //Are we editing within a selection group? if (groupEditing || (av.getSelectionGroup() != null && av.getSelectionGroup().sequences.contains(seq))) { fixedColumns = true; fixedLeft = av.getSelectionGroup().getStartRes(); fixedRight = av.getSelectionGroup().getEndRes(); if ( (startres < fixedLeft && lastres >= fixedLeft) || (startres >= fixedLeft && lastres < fixedLeft) || (startres > fixedRight && lastres <=fixedRight) || (startres <= fixedRight && lastres > fixedRight)) { endEditing(); return; } if (fixedLeft > startres) { fixedRight = fixedLeft - 1; fixedLeft = 0; } else if (fixedRight < startres) { fixedLeft = fixedRight; fixedRight = -1; } } if (groupEditing) { SequenceGroup sg = av.getSelectionGroup(); if(fixedLeft>sg.getEndRes() ||fixedRight