/* * 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.analysis.*; 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; /** * 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) { if (av.getWrapAlignment()) { return; } doMouseMoved(evt); } public void mouseDragged(MouseEvent evt) { if (av.getWrapAlignment()) { return; } if (editingSeqs) { doMouseDragged(evt); } else { doMouseDraggedDefineMode(evt); } } }); addMouseListener(new MouseAdapter() { public void mouseReleased(MouseEvent evt) { if (av.getWrapAlignment()) { return; } if (editingSeqs) { doMouseReleased(evt); } else { doMouseReleasedDefineMode(evt); } } public void mousePressed(MouseEvent evt) { if (av.getWrapAlignment()) { 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 (av.getWrapAlignment() || editingSeqs) { return; } doMouseExitedDefineMode(evt); } public void mouseEntered(MouseEvent evt) { if (av.getWrapAlignment() || editingSeqs) { return; } doMouseEnteredDefineMode(evt); } }); } /** * 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; int res; int x = evt.getX(); int y = evt.getY(); res = (x / av.getCharWidth()) + av.getStartRes(); seq = (y / av.getCharHeight()) + av.getStartSeq(); 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 = 0; int seq = 0; int x = evt.getX(); int y = evt.getY(); if (av.wrapAlignment) { y -= (2 * av.charHeight); int chunkHeight = (av.getAlignment().getHeight() + 2) * av.charHeight; res = (int) ((y / chunkHeight) * (getWidth() / av.charWidth)) + (x / av.getCharWidth()) + av.getStartRes(); y %= chunkHeight; seq = (y / av.getCharHeight()) + av.getStartSeq(); } else { res = (x / av.getCharWidth()) + av.getStartRes(); seq = (y / av.getCharHeight()) + av.getStartSeq(); } if (seq >= av.getAlignment().getHeight()) { return; } SequenceI sequence = av.getAlignment().getSequenceAt(seq); if (res > sequence.getLength()) { return; } Object obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + ""); String aa = ""; if (obj != null) { aa = obj.toString(); } StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: " + sequence.getName()); if (aa != "") { text.append(" Residue: " + aa + " (" + 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.getSequenceFeatures(); Enumeration e = features.elements(); StringBuffer sbuffer = new StringBuffer(); while (e.hasMoreElements()) { SequenceFeature sf = (SequenceFeature) e.nextElement(); if ((sf.getStart() <= sequence.findPosition(res)) && (sf.getEnd() >= sequence.findPosition(res))) { if (sbuffer.length() > 0) { sbuffer.append("; "); } sbuffer.append(sf.getType() + " " + sf.getDescription()); if (sf.getStatus().length() > 0) { sbuffer.append(" (" + sf.getStatus() + ")"); } } } this.setToolTipText(sbuffer.toString()); } } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void doMouseDragged(MouseEvent evt) { // If we're dragging we're editing int res = (evt.getX() / av.getCharWidth()) + av.getStartRes(); 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.updateConservation(); av.updateConsensus(); // Y O Y CLUSTALX ColourSchemeI cs = av.getGlobalColourScheme(); if (av.getSelectionGroup() != null) { av.getSelectionGroup().recalcConservation(); } if (cs.conservationApplied()) { Conservation c = new Conservation("All", ResidueProperties.propHash, 3, av.alignment.getSequences(), 0, av.alignment.getWidth() - 1); c.calculate(); c.verdict(false, av.ConsPercGaps); if (cs instanceof ClustalxColourScheme) { ClustalxColourScheme cxs = (ClustalxColourScheme) cs; cxs.resetClustalX(av.alignment.getSequences(), av.alignment.getWidth()); cs.setConservation(c); av.setGlobalColourScheme(cs); } else { cs.setConservation(c); av.setGlobalColourScheme(cs); } } if (cs instanceof ClustalxColourScheme) { ((ClustalxColourScheme) cs).resetClustalX(av.alignment.getSequences(), av.alignment.getWidth()); av.setGlobalColourScheme(cs); } } /** * DOCUMENT ME! * * @param evt DOCUMENT ME! */ public void doMousePressedDefineMode(MouseEvent evt) { int res = (evt.getX() / av.getCharWidth()) + av.getStartRes(); int seq = (evt.getY() / av.getCharHeight()) + av.getStartSeq(); oldSeq = seq; 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 = (evt.getX() / av.getCharWidth()) + av.getStartRes(); int y = (evt.getY() / av.getCharHeight()) + av.getStartSeq(); 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()) { 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(75); } catch (Exception ex) { } } } } }