/* * 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.appletgui; import java.util.*; import java.awt.*; import java.awt.event.*; import jalview.datamodel.*; import jalview.schemes.*; public class SeqPanel extends Panel { public SeqCanvas seqCanvas; 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; public SeqPanel(AlignViewport avp, AlignmentPanel p) { this.av = avp; seqCanvas = new SeqCanvas(avp); setLayout(new BorderLayout()); add(seqCanvas); ap = p; seqCanvas.addMouseMotionListener(new MouseMotionAdapter() { public void mouseMoved(MouseEvent evt) { doMouseMoved(evt); } public void mouseDragged(MouseEvent evt) { if (editingSeqs) { doMouseDragged(evt); } else { doMouseDraggedDefineMode(evt); } } }); seqCanvas.addMouseListener(new MouseAdapter() { public void mouseReleased(MouseEvent evt) { if (editingSeqs) { doMouseReleased(evt); } else { doMouseReleasedDefineMode(evt); } } public void mousePressed(MouseEvent evt) { 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); } }); seqCanvas.repaint(); } public void doMouseReleased(MouseEvent evt) { if (seqEditOccurred > -1) { editOccurred(seqEditOccurred); } startseq = -1; lastres = -1; seqEditOccurred = -1; editingSeqs = false; groupEditing = false; ap.repaint(); } int findRes(MouseEvent evt) { int res = 0; int x = evt.getX(); if (av.wrapAlignment) { int y = evt.getY(); y -= (2 * av.charHeight); x -= seqCanvas.LABEL_WEST; int chunkHeight = (av.getAlignment().getHeight() + 2) * av.charHeight; int cwidth = seqCanvas.getWrappedCanvasWidth(this.getSize().width); int block = y/chunkHeight; block += av.getStartRes()/cwidth; res = block*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) { y -= (2 * av.charHeight); int chunkHeight = (av.getAlignment().getHeight() + 2) * av.charHeight; seq = ( (y % chunkHeight) / av.getCharHeight()); } else { seq = (y / av.getCharHeight()) + av.getStartSeq(); } return seq; } 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 < av.getAlignment().getHeight() && res < av.getAlignment().getSequenceAt(seq).getLength()) { //char resstr = align.getSequenceAt(seq).getSequence().charAt(res); // Find the residue's position in the sequence (res is the position // in the alignment startseq = seq; lastres = res; } else { startseq = -1; lastres = -1; } return; } public void doMouseMoved(MouseEvent evt) { int res = findRes(evt); int seq = findSeq(evt); if (seq >= av.getAlignment().getHeight() || seq<0) { 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() + ")"); } } } } } 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(); } 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(); } public void insertChar(int j, int seq) { av.alignment.getSequenceAt(seq).insertCharAt(j, av.getGapCharacter()); seqEditOccurred = seq; } public void deleteChar(int j, int seq) { av.alignment.getSequenceAt(seq).deleteCharAt(j); seqEditOccurred = seq; av.alignment.getWidth(); repaint(); } void editOccurred(int i) { if (endEdit == startEdit) { ap.alignFrame.historyList.pop(); ap.alignFrame.updateEditMenuBar(); } av.updateConservation(); av.updateConsensus(); //Does the edit affect any groups? ////////////////////////////// Vector groupsToUpdate = new Vector(); SequenceGroup sg = av.getSelectionGroup(); int s, g, gSize = av.alignment.getGroups().size(); if (sg != null) { groupsToUpdate.addElement(av.getSelectionGroup()); int sSize = sg.getSize(); for (s = 0; s < sSize; s++) { SequenceI seq = sg.getSequenceAt(s); for (g = 0; g < gSize; g++) { SequenceGroup sg2 = (SequenceGroup) av.alignment.getGroups(). elementAt( g); if (sg2.sequences.contains(seq)) { if (!groupsToUpdate.contains(sg2)) groupsToUpdate.addElement(sg2); continue; } } } } else { SequenceI seq = av.alignment.getSequenceAt(startseq); for (g = 0; g < gSize; g++) { SequenceGroup sg2 = (SequenceGroup) av.alignment.getGroups().elementAt( g); if (sg2.sequences.contains(seq)) { if (!groupsToUpdate.contains(sg2)) groupsToUpdate.addElement(sg2); continue; } } } gSize = groupsToUpdate.size(); for (g = 0; g < gSize; g++) { ( (SequenceGroup) groupsToUpdate.elementAt(g)).recalcConservation(); } ////End of updating groups ////////////////////////////////////////// // Y O Y CLUSTALX ColourSchemeI cs = av.getGlobalColourScheme(); if (av.getSelectionGroup() != null) { av.getSelectionGroup().recalcConservation(); } if(cs!=null) { if (cs.conservationApplied()) { jalview.analysis.Conservation c = new jalview.analysis.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)cs). 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); } } } ////////////////////////////////////////// /////Everything below this is for defining the boundary of the rubberband ////////////////////////////////////////// int oldSeq = -1; public void doMousePressedDefineMode(MouseEvent evt) { int res = findRes(evt); int seq = findSeq(evt); 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"); } } // DETECT RIGHT MOUSE BUTTON IN AWT else if ( (evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK) { APopupMenu popup = new APopupMenu(ap, null, null); this.add(popup); popup.show(this, evt.getX(), evt.getY()); } 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; } } boolean changeEndSeq = false; boolean changeStartSeq = false; boolean changeEndRes = false; boolean changeStartRes = false; SequenceGroup stretchGroup = null; public void doMouseReleasedDefineMode(MouseEvent evt) { if(mouseDragging) { stretchGroup.recalcConservation(); mouseDragging = false; } if (stretchGroup == null) { return; } if(stretchGroup.cs!=null) { 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; seqCanvas.repaint(); ap.repaint(); } boolean remove = false; public void doMouseDraggedDefineMode(MouseEvent evt) { int res = findRes(evt); int y = findSeq(evt); if(y>=av.alignment.getHeight()) y = av.alignment.getHeight()-1; if (stretchGroup == null) { return; } if (res > av.alignment.getWidth() && !av.getWrapAlignment()) { 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(); } public void doMouseEnteredDefineMode(MouseEvent e) { if (scrollThread != null) { scrollThread.running = false; } } public void doMouseExitedDefineMode(MouseEvent e) { 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() >= getSize().height && av.alignment.getHeight() > av.getEndSeq()) { running = ap.scrollUp(false); } if (mouseDragging && evt.getX() < 0) { running = ap.scrollRight(true); } else if (mouseDragging && evt.getX() >= getSize().width) { running = ap.scrollRight(false); } } try { Thread.sleep(75); } catch (Exception ex) {} } } } }