2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3 * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 import java.awt.event.*;
27 import jalview.commands.*;
28 import jalview.datamodel.*;
29 import jalview.schemes.*;
30 import jalview.structure.*;
38 public class SeqPanel extends JPanel implements MouseListener,
39 MouseMotionListener, MouseWheelListener, SequenceListener
43 public SeqCanvas seqCanvas;
46 public AlignmentPanel ap;
48 protected int lastres;
50 protected int startseq;
52 protected AlignViewport av;
54 ScrollThread scrollThread = null;
56 boolean mouseDragging = false;
58 boolean editingSeqs = false;
60 boolean groupEditing = false;
62 // ////////////////////////////////////////
63 // ///Everything below this is for defining the boundary of the rubberband
64 // ////////////////////////////////////////
67 boolean changeEndSeq = false;
69 boolean changeStartSeq = false;
71 boolean changeEndRes = false;
73 boolean changeStartRes = false;
75 SequenceGroup stretchGroup = null;
77 boolean remove = false;
81 boolean mouseWheelPressed = false;
83 StringBuffer keyboardNo1;
85 StringBuffer keyboardNo2;
87 java.net.URL linkImageURL;
89 StringBuffer tooltipText = new StringBuffer("<html>");
93 EditCommand editCommand;
95 StructureSelectionManager ssm;
98 * Creates a new SeqPanel object.
105 public SeqPanel(AlignViewport av, AlignmentPanel ap)
107 linkImageURL = getClass().getResource("/images/link.gif");
108 ToolTipManager.sharedInstance().registerComponent(this);
109 ToolTipManager.sharedInstance().setInitialDelay(0);
110 ToolTipManager.sharedInstance().setDismissDelay(10000);
112 setBackground(Color.white);
114 seqCanvas = new SeqCanvas(ap);
115 setLayout(new BorderLayout());
116 add(seqCanvas, BorderLayout.CENTER);
122 addMouseMotionListener(this);
123 addMouseListener(this);
124 addMouseWheelListener(this);
125 ssm = StructureSelectionManager.getStructureSelectionManager();
126 ssm.addStructureViewerListener(this);
130 int startWrapBlock = -1;
132 int wrappedBlock = -1;
134 int findRes(MouseEvent evt)
139 if (av.wrapAlignment)
142 int hgap = av.charHeight;
143 if (av.scaleAboveWrapped)
145 hgap += av.charHeight;
148 int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
149 + seqCanvas.getAnnotationHeight();
153 x -= seqCanvas.LABEL_WEST;
155 int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());
161 wrappedBlock = y / cHeight;
162 wrappedBlock += av.getStartRes() / cwidth;
164 res = wrappedBlock * cwidth + x / av.getCharWidth();
169 res = (x / av.getCharWidth()) + av.getStartRes();
172 if (av.hasHiddenColumns)
174 res = av.getColumnSelection().adjustForHiddenColumns(res);
181 int findSeq(MouseEvent evt)
186 if (av.wrapAlignment)
188 int hgap = av.charHeight;
189 if (av.scaleAboveWrapped)
191 hgap += av.charHeight;
194 int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
195 + seqCanvas.getAnnotationHeight();
199 seq = Math.min((y % cHeight) / av.getCharHeight(), av.alignment
204 seq = Math.min((y / av.getCharHeight()) + av.getStartSeq(),
205 av.alignment.getHeight() - 1);
211 SequenceFeature[] findFeaturesAtRes(SequenceI sequence, int res)
213 Vector tmp = new Vector();
214 SequenceFeature[] features = sequence.getSequenceFeatures();
215 if (features != null)
217 for (int i = 0; i < features.length; i++)
219 if (av.featuresDisplayed == null
220 || !av.featuresDisplayed.containsKey(features[i].getType()))
225 if (features[i].featureGroup != null
226 && seqCanvas.fr.featureGroups != null
227 && seqCanvas.fr.featureGroups
228 .containsKey(features[i].featureGroup)
229 && !((Boolean) seqCanvas.fr.featureGroups
230 .get(features[i].featureGroup)).booleanValue())
233 if ((features[i].getBegin() <= res)
234 && (features[i].getEnd() >= res))
236 tmp.addElement(features[i]);
241 features = new SequenceFeature[tmp.size()];
242 tmp.copyInto(features);
249 if (editCommand != null && editCommand.getSize() > 0)
251 ap.alignFrame.addHistoryItem(editCommand);
252 av.firePropertyChange("alignment", null, av.getAlignment()
259 groupEditing = false;
267 seqCanvas.cursorY = getKeyboardNo1() - 1;
271 void setCursorColumn()
273 seqCanvas.cursorX = getKeyboardNo1() - 1;
277 void setCursorRowAndColumn()
279 if (keyboardNo2 == null)
281 keyboardNo2 = new StringBuffer();
285 seqCanvas.cursorX = getKeyboardNo1() - 1;
286 seqCanvas.cursorY = getKeyboardNo2() - 1;
291 void setCursorPosition()
293 SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(
296 seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1() - 1);
300 void moveCursor(int dx, int dy)
302 seqCanvas.cursorX += dx;
303 seqCanvas.cursorY += dy;
304 if (av.hasHiddenColumns && !av.colSel.isVisible(seqCanvas.cursorX))
306 int original = seqCanvas.cursorX - dx;
307 int maxWidth = av.alignment.getWidth();
309 while (!av.colSel.isVisible(seqCanvas.cursorX)
310 && seqCanvas.cursorX < maxWidth && seqCanvas.cursorX > 0)
312 seqCanvas.cursorX += dx;
315 if (seqCanvas.cursorX >= maxWidth
316 || !av.colSel.isVisible(seqCanvas.cursorX))
318 seqCanvas.cursorX = original;
325 void scrollToVisible()
327 if (seqCanvas.cursorX < 0)
329 seqCanvas.cursorX = 0;
331 else if (seqCanvas.cursorX > av.alignment.getWidth() - 1)
333 seqCanvas.cursorX = av.alignment.getWidth() - 1;
336 if (seqCanvas.cursorY < 0)
338 seqCanvas.cursorY = 0;
340 else if (seqCanvas.cursorY > av.alignment.getHeight() - 1)
342 seqCanvas.cursorY = av.alignment.getHeight() - 1;
346 if (av.wrapAlignment)
348 ap.scrollToWrappedVisible(seqCanvas.cursorX);
352 while (seqCanvas.cursorY < av.startSeq)
356 while (seqCanvas.cursorY + 1 > av.endSeq)
360 if (!av.wrapAlignment)
362 while (seqCanvas.cursorX < av.colSel
363 .adjustForHiddenColumns(av.startRes))
365 if (!ap.scrollRight(false))
370 while (seqCanvas.cursorX > av.colSel
371 .adjustForHiddenColumns(av.endRes))
373 if (!ap.scrollRight(true))
380 setStatusMessage(av.alignment.getSequenceAt(seqCanvas.cursorY),
381 seqCanvas.cursorX, seqCanvas.cursorY);
386 void setSelectionAreaAtCursor(boolean topLeft)
388 SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(
391 if (av.getSelectionGroup() != null)
393 SequenceGroup sg = av.selectionGroup;
394 // Find the top and bottom of this group
395 int min = av.alignment.getHeight(), max = 0;
396 for (int i = 0; i < sg.getSize(); i++)
398 int index = av.alignment.findIndex(sg.getSequenceAt(i));
413 sg.setStartRes(seqCanvas.cursorX);
414 if (sg.getEndRes() < seqCanvas.cursorX)
416 sg.setEndRes(seqCanvas.cursorX);
419 min = seqCanvas.cursorY;
423 sg.setEndRes(seqCanvas.cursorX);
424 if (sg.getStartRes() > seqCanvas.cursorX)
426 sg.setStartRes(seqCanvas.cursorX);
429 max = seqCanvas.cursorY + 1;
434 // Only the user can do this
435 av.setSelectionGroup(null);
439 // Now add any sequences between min and max
440 sg.getSequences(null).clear();
441 for (int i = min; i < max; i++)
443 sg.addSequence(av.alignment.getSequenceAt(i), false);
448 if (av.getSelectionGroup() == null)
450 SequenceGroup sg = new SequenceGroup();
451 sg.setStartRes(seqCanvas.cursorX);
452 sg.setEndRes(seqCanvas.cursorX);
453 sg.addSequence(sequence, false);
454 av.setSelectionGroup(sg);
457 ap.paintAlignment(false);
460 void insertGapAtCursor(boolean group)
462 groupEditing = group;
463 startseq = seqCanvas.cursorY;
464 lastres = seqCanvas.cursorX;
465 editSequence(true, seqCanvas.cursorX + getKeyboardNo1());
469 void deleteGapAtCursor(boolean group)
471 groupEditing = group;
472 startseq = seqCanvas.cursorY;
473 lastres = seqCanvas.cursorX + getKeyboardNo1();
474 editSequence(false, seqCanvas.cursorX);
478 void numberPressed(char value)
480 if (keyboardNo1 == null)
482 keyboardNo1 = new StringBuffer();
485 if (keyboardNo2 != null)
487 keyboardNo2.append(value);
491 keyboardNo1.append(value);
497 if (keyboardNo1 == null)
501 int value = Integer.parseInt(keyboardNo1.toString());
509 if (keyboardNo2 == null)
513 int value = Integer.parseInt(keyboardNo2.toString());
525 public void mouseReleased(MouseEvent evt)
527 mouseDragging = false;
528 mouseWheelPressed = false;
532 doMouseReleasedDefineMode(evt);
545 public void mousePressed(MouseEvent evt)
547 lastMousePress = evt.getPoint();
549 if (javax.swing.SwingUtilities.isMiddleMouseButton(evt))
551 mouseWheelPressed = true;
555 if (evt.isShiftDown() || evt.isAltDown() || evt.isControlDown())
557 if (evt.isAltDown() || evt.isControlDown())
565 doMousePressedDefineMode(evt);
569 int seq = findSeq(evt);
570 int res = findRes(evt);
572 if (seq < 0 || res < 0)
577 if ((seq < av.getAlignment().getHeight())
578 && (res < av.getAlignment().getSequenceAt(seq).getLength()))
594 public void mouseOverSequence(SequenceI sequence, int index, int pos)
596 String tmp = sequence.hashCode() + " " + index + " " + pos;
598 if (lastMessage == null || !lastMessage.equals(tmp))
600 // System.err.println("mouseOver Sequence: "+tmp);
601 ssm.mouseOverSequence(sequence, index, pos);
606 public void highlightSequence(SearchResults results)
608 seqCanvas.highlightSearchResults(results);
611 public void updateColours(SequenceI seq, int index)
613 System.out.println("update the seqPanel colours");
623 public void mouseMoved(MouseEvent evt)
627 // This is because MacOSX creates a mouseMoved
628 // If control is down, other platforms will not.
632 int res = findRes(evt);
633 int seq = findSeq(evt);
635 if (res < 0 || seq < 0 || seq >= av.getAlignment().getHeight())
640 SequenceI sequence = av.getAlignment().getSequenceAt(seq);
642 if (res >= sequence.getLength())
647 pos = setStatusMessage(sequence, res, seq);
648 if (ssm != null && pos > -1)
649 mouseOverSequence(sequence, res, pos);
651 tooltipText.setLength(6); // Cuts the buffer back to <html>
653 SequenceGroup[] groups = av.alignment.findAllGroups(sequence);
656 for (int g = 0; g < groups.length; g++)
658 if (groups[g].getStartRes() <= res && groups[g].getEndRes() >= res)
660 if (tooltipText.length() > 6)
662 tooltipText.append("<br>");
665 if (!groups[g].getName().startsWith("JTreeGroup")
666 && !groups[g].getName().startsWith("JGroup"))
668 tooltipText.append(groups[g].getName());
671 if (groups[g].getDescription() != null)
673 tooltipText.append(": " + groups[g].getDescription());
679 // use aa to see if the mouse pointer is on a
680 if (av.showSequenceFeatures)
683 SequenceFeature[] features = findFeaturesAtRes(sequence
684 .getDatasetSequence(), rpos = sequence.findPosition(res));
685 appendFeatures(tooltipText, linkImageURL.toString(), rpos, features);
687 if (tooltipText.length() == 6) // <html></html>
689 setToolTipText(null);
693 tooltipText.append("</html>");
694 if (lastTooltip == null
695 || !lastTooltip.equals(tooltipText.toString()))
696 setToolTipText(tooltipText.toString());
698 lastTooltip = tooltipText.toString();
703 * appends the features at rpos to the given stringbuffer ready for display in a tooltip
704 * @param tooltipText2
705 * @param linkImageURL
708 * TODO refactor to Jalview 'utilities' somehow.
710 public static void appendFeatures(StringBuffer tooltipText2, String linkImageURL,
711 int rpos, SequenceFeature[] features)
714 if (features != null)
716 for (int i = 0; i < features.length; i++)
718 if (features[i].getType().equals("disulfide bond"))
720 if (features[i].getBegin() == rpos
721 || features[i].getEnd() == rpos)
723 if (tooltipText2.length() > 6)
725 tooltipText2.append("<br>");
727 tooltipText2.append("disulfide bond " + features[i].getBegin()
728 + ":" + features[i].getEnd());
729 if (features[i].links != null)
731 tooltipText2.append(" <img src=\"" + linkImageURL + "\">");
737 if (tooltipText2.length() > 6)
739 tooltipText2.append("<br>");
742 tooltipText2.append(features[i].getType() + " ");
745 // we are marking a positional feature
746 tooltipText2.append(features[i].begin);
748 if (features[i].begin != features[i].end)
750 tooltipText2.append(" " + features[i].end);
753 if (features[i].getDescription() != null
754 && !features[i].description.equals(features[i]
757 tmpString = features[i].getDescription();
758 int startTag = tmpString.toUpperCase().indexOf("<HTML>");
761 tmpString = tmpString.substring(startTag + 6);
763 int endTag = tmpString.toUpperCase().indexOf("</BODY>");
766 tmpString = tmpString.substring(0, endTag);
768 endTag = tmpString.toUpperCase().indexOf("</HTML>");
771 tmpString = tmpString.substring(0, endTag);
776 tooltipText2.append("; " + tmpString);
780 if (tmpString.indexOf("<") > -1
781 || tmpString.indexOf(">") > -1)
783 // The description does not specify html is to
784 // be used, so we must remove < > symbols
785 tmpString = tmpString.replaceAll("<", "<");
786 tmpString = tmpString.replaceAll(">", ">");
788 tooltipText2.append("; ");
789 tooltipText2.append(tmpString);
794 tooltipText2.append("; " + tmpString);
798 if (features[i].getValue("status") != null)
800 String status = features[i].getValue("status").toString();
801 if (status.length() > 0)
803 tooltipText2.append("; (" + features[i].getValue("status")
808 if (features[i].links != null)
810 tooltipText2.append(" <img src=\"" + linkImageURL + "\">");
821 * Set status message in alignment panel
824 * aligned sequence object
828 * index of sequence in alignment
829 * @return position of res in sequence
831 int setStatusMessage(SequenceI sequence, int res, int seq)
834 StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: "
835 + sequence.getName());
838 if (av.alignment.isNucleotide())
840 obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)
844 text.append(" Nucleotide: ");
849 obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
852 text.append(" Residue: ");
858 pos = sequence.findPosition(res);
861 text.append(obj + " (" + pos + ")");
864 ap.alignFrame.statusBar.setText(text.toString());
874 public void mouseDragged(MouseEvent evt)
876 if (mouseWheelPressed)
878 int oldWidth = av.charWidth;
880 // Which is bigger, left-right or up-down?
881 if (Math.abs(evt.getY() - lastMousePress.getY()) > Math.abs(evt
883 - lastMousePress.getX()))
885 int fontSize = av.font.getSize();
887 if (evt.getY() < lastMousePress.getY())
891 else if (evt.getY() > lastMousePress.getY())
902 .setFont(new Font(av.font.getName(), av.font.getStyle(),
904 av.charWidth = oldWidth;
909 if (evt.getX() < lastMousePress.getX() && av.charWidth > 1)
913 else if (evt.getX() > lastMousePress.getX())
918 ap.paintAlignment(false);
921 FontMetrics fm = getFontMetrics(av.getFont());
922 av.validCharWidth = fm.charWidth('M') <= av.charWidth;
924 lastMousePress = evt.getPoint();
931 doMouseDraggedDefineMode(evt);
935 int res = findRes(evt);
942 if ((lastres == -1) || (lastres == res))
947 if ((res < av.getAlignment().getWidth()) && (res < lastres))
949 // dragLeft, delete gap
950 editSequence(false, res);
954 editSequence(true, res);
957 mouseDragging = true;
958 if (scrollThread != null)
960 scrollThread.setEvent(evt);
964 synchronized void editSequence(boolean insertGap, int startres)
968 boolean fixedColumns = false;
969 SequenceGroup sg = av.getSelectionGroup();
971 SequenceI seq = av.alignment.getSequenceAt(startseq);
973 // No group, but the sequence may represent a group
974 if (!groupEditing && av.hasHiddenRows)
976 if (av.hiddenRepSequences != null
977 && av.hiddenRepSequences.containsKey(seq))
979 sg = (SequenceGroup) av.hiddenRepSequences.get(seq);
984 StringBuffer message = new StringBuffer();
987 message.append("Edit group:");
988 if (editCommand == null)
990 editCommand = new EditCommand("Edit Group");
995 message.append("Edit sequence: " + seq.getName());
996 String label = seq.getName();
997 if (label.length() > 10)
999 label = label.substring(0, 10);
1001 if (editCommand == null)
1003 editCommand = new EditCommand("Edit " + label);
1009 message.append(" insert ");
1013 message.append(" delete ");
1016 message.append(Math.abs(startres - lastres) + " gaps.");
1017 ap.alignFrame.statusBar.setText(message.toString());
1019 // Are we editing within a selection group?
1021 || (sg != null && sg.getSequences(av.hiddenRepSequences)
1024 fixedColumns = true;
1026 // sg might be null as the user may only see 1 sequence,
1027 // but the sequence represents a group
1030 if (av.hiddenRepSequences == null
1031 || !av.hiddenRepSequences.containsKey(seq))
1036 sg = (SequenceGroup) av.hiddenRepSequences.get(seq);
1039 fixedLeft = sg.getStartRes();
1040 fixedRight = sg.getEndRes();
1042 if ((startres < fixedLeft && lastres >= fixedLeft)
1043 || (startres >= fixedLeft && lastres < fixedLeft)
1044 || (startres > fixedRight && lastres <= fixedRight)
1045 || (startres <= fixedRight && lastres > fixedRight))
1051 if (fixedLeft > startres)
1053 fixedRight = fixedLeft - 1;
1056 else if (fixedRight < startres)
1058 fixedLeft = fixedRight;
1063 if (av.hasHiddenColumns)
1065 fixedColumns = true;
1066 int y1 = av.getColumnSelection().getHiddenBoundaryLeft(startres);
1067 int y2 = av.getColumnSelection().getHiddenBoundaryRight(startres);
1069 if ((insertGap && startres > y1 && lastres < y1)
1070 || (!insertGap && startres < y2 && lastres > y2))
1076 // System.out.print(y1+" "+y2+" "+fixedLeft+" "+fixedRight+"~~");
1077 // Selection spans a hidden region
1078 if (fixedLeft < y1 && (fixedRight > y2 || fixedRight == -1))
1086 fixedRight = y2 - 1;
1093 Vector vseqs = sg.getSequences(av.hiddenRepSequences);
1094 int g, groupSize = vseqs.size();
1095 SequenceI[] groupSeqs = new SequenceI[groupSize];
1096 for (g = 0; g < groupSeqs.length; g++)
1098 groupSeqs[g] = (SequenceI) vseqs.elementAt(g);
1104 // If the user has selected the whole sequence, and is dragging to
1105 // the right, we can still extend the alignment and selectionGroup
1106 if (sg.getStartRes() == 0 && sg.getEndRes() == fixedRight
1107 && sg.getEndRes() == av.alignment.getWidth() - 1)
1109 sg.setEndRes(av.alignment.getWidth() + startres - lastres);
1110 fixedRight = sg.getEndRes();
1113 // Is it valid with fixed columns??
1114 // Find the next gap before the end
1115 // of the visible region boundary
1116 boolean blank = false;
1117 for (fixedRight = fixedRight; fixedRight > lastres; fixedRight--)
1121 for (g = 0; g < groupSize; g++)
1123 for (int j = 0; j < startres - lastres; j++)
1125 if (!jalview.util.Comparison.isGap(groupSeqs[g]
1126 .getCharAt(fixedRight - j)))
1141 if (sg.getSize() == av.alignment.getHeight())
1143 if ((av.hasHiddenColumns && startres < av.getColumnSelection()
1144 .getHiddenBoundaryRight(startres)))
1150 int alWidth = av.alignment.getWidth();
1151 if (av.hasHiddenRows)
1153 int hwidth = av.alignment.getHiddenSequences().getWidth();
1154 if (hwidth > alWidth)
1159 // We can still insert gaps if the selectionGroup
1160 // contains all the sequences
1161 sg.setEndRes(sg.getEndRes() + startres - lastres);
1162 fixedRight = alWidth + startres - lastres;
1173 else if (!insertGap)
1175 // / Are we able to delete?
1176 // ie are all columns blank?
1178 for (g = 0; g < groupSize; g++)
1180 for (int j = startres; j < lastres; j++)
1182 if (groupSeqs[g].getLength() <= j)
1187 if (!jalview.util.Comparison.isGap(groupSeqs[g].getCharAt(j)))
1189 // Not a gap, block edit not valid
1199 // dragging to the right
1200 if (fixedColumns && fixedRight != -1)
1202 for (int j = lastres; j < startres; j++)
1204 insertChar(j, groupSeqs, fixedRight);
1209 editCommand.appendEdit(EditCommand.INSERT_GAP, groupSeqs,
1210 startres, startres - lastres, av.alignment, true);
1215 // dragging to the left
1216 if (fixedColumns && fixedRight != -1)
1218 for (int j = lastres; j > startres; j--)
1220 deleteChar(startres, groupSeqs, fixedRight);
1225 editCommand.appendEdit(EditCommand.DELETE_GAP, groupSeqs,
1226 startres, lastres - startres, av.alignment, true);
1232 // ///Editing a single sequence///////////
1236 // dragging to the right
1237 if (fixedColumns && fixedRight != -1)
1239 for (int j = lastres; j < startres; j++)
1241 insertChar(j, new SequenceI[]
1242 { seq }, fixedRight);
1247 editCommand.appendEdit(EditCommand.INSERT_GAP, new SequenceI[]
1248 { seq }, lastres, startres - lastres, av.alignment, true);
1253 // dragging to the left
1254 if (fixedColumns && fixedRight != -1)
1256 for (int j = lastres; j > startres; j--)
1258 if (!jalview.util.Comparison.isGap(seq.getCharAt(startres)))
1263 deleteChar(startres, new SequenceI[]
1264 { seq }, fixedRight);
1269 // could be a keyboard edit trying to delete none gaps
1271 for (int m = startres; m < lastres; m++)
1273 if (!jalview.util.Comparison.isGap(seq.getCharAt(m)))
1282 editCommand.appendEdit(EditCommand.DELETE_GAP, new SequenceI[]
1283 { seq }, startres, max, av.alignment, true);
1290 seqCanvas.repaint();
1293 void insertChar(int j, SequenceI[] seq, int fixedColumn)
1295 int blankColumn = fixedColumn;
1296 for (int s = 0; s < seq.length; s++)
1298 // Find the next gap before the end of the visible region boundary
1299 // If lastCol > j, theres a boundary after the gap insertion
1301 for (blankColumn = fixedColumn; blankColumn > j; blankColumn--)
1303 if (jalview.util.Comparison.isGap(seq[s].getCharAt(blankColumn)))
1305 // Theres a space, so break and insert the gap
1310 if (blankColumn <= j)
1312 blankColumn = fixedColumn;
1318 editCommand.appendEdit(EditCommand.DELETE_GAP, seq, blankColumn, 1,
1319 av.alignment, true);
1321 editCommand.appendEdit(EditCommand.INSERT_GAP, seq, j, 1, av.alignment,
1326 void deleteChar(int j, SequenceI[] seq, int fixedColumn)
1329 editCommand.appendEdit(EditCommand.DELETE_GAP, seq, j, 1, av.alignment,
1332 editCommand.appendEdit(EditCommand.INSERT_GAP, seq, fixedColumn, 1,
1333 av.alignment, true);
1342 public void mouseEntered(MouseEvent e)
1349 if (scrollThread != null)
1351 scrollThread.running = false;
1352 scrollThread = null;
1362 public void mouseExited(MouseEvent e)
1364 if (av.getWrapAlignment())
1371 scrollThread = new ScrollThread();
1375 public void mouseClicked(MouseEvent evt)
1377 SequenceI sequence = av.alignment.getSequenceAt(findSeq(evt));
1378 if (evt.getClickCount() > 1)
1380 if (av.getSelectionGroup().getSize() == 1
1381 && av.getSelectionGroup().getEndRes()
1382 - av.getSelectionGroup().getStartRes() < 2)
1384 av.setSelectionGroup(null);
1387 SequenceFeature[] features = findFeaturesAtRes(sequence
1388 .getDatasetSequence(), sequence.findPosition(findRes(evt)));
1390 if (features != null && features.length > 0)
1392 SearchResults highlight = new SearchResults();
1393 highlight.addResult(sequence, features[0].getBegin(), features[0]
1395 seqCanvas.highlightSearchResults(highlight);
1397 if (features != null && features.length > 0)
1399 seqCanvas.getFeatureRenderer().amendFeatures(new SequenceI[]
1400 { sequence }, features, false, ap);
1402 seqCanvas.highlightSearchResults(null);
1407 public void mouseWheelMoved(MouseWheelEvent e)
1410 if (e.getWheelRotation() > 0)
1426 public void doMousePressedDefineMode(MouseEvent evt)
1428 int res = findRes(evt);
1429 int seq = findSeq(evt);
1432 startWrapBlock = wrappedBlock;
1434 if (av.wrapAlignment && seq > av.alignment.getHeight())
1436 JOptionPane.showInternalMessageDialog(Desktop.desktop,
1437 "Cannot edit annotations in wrapped view.",
1438 "Wrapped view - no edit", JOptionPane.WARNING_MESSAGE);
1442 if (seq < 0 || res < 0)
1447 SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seq);
1449 if ((sequence == null) || (res > sequence.getLength()))
1454 stretchGroup = av.getSelectionGroup();
1456 if (stretchGroup == null)
1458 stretchGroup = av.alignment.findGroup(sequence);
1460 if ((stretchGroup != null) && (res > stretchGroup.getStartRes())
1461 && (res < stretchGroup.getEndRes()))
1463 av.setSelectionGroup(stretchGroup);
1467 stretchGroup = null;
1470 else if (!stretchGroup.getSequences(null).contains(sequence)
1471 || (stretchGroup.getStartRes() > res)
1472 || (stretchGroup.getEndRes() < res))
1474 stretchGroup = null;
1476 SequenceGroup[] allGroups = av.alignment.findAllGroups(sequence);
1478 if (allGroups != null)
1480 for (int i = 0; i < allGroups.length; i++)
1482 if ((allGroups[i].getStartRes() <= res)
1483 && (allGroups[i].getEndRes() >= res))
1485 stretchGroup = allGroups[i];
1491 av.setSelectionGroup(stretchGroup);
1495 if (javax.swing.SwingUtilities.isRightMouseButton(evt))
1497 SequenceFeature[] allFeatures = findFeaturesAtRes(sequence
1498 .getDatasetSequence(), sequence.findPosition(res));
1499 Vector links = new Vector();
1500 for (int i = 0; i < allFeatures.length; i++)
1502 if (allFeatures[i].links != null)
1504 for (int j = 0; j < allFeatures[i].links.size(); j++)
1506 links.addElement(allFeatures[i].links.elementAt(j));
1511 jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu(ap, null, links);
1512 pop.show(this, evt.getX(), evt.getY());
1518 seqCanvas.cursorX = findRes(evt);
1519 seqCanvas.cursorY = findSeq(evt);
1520 seqCanvas.repaint();
1524 if (stretchGroup == null)
1526 // Only if left mouse button do we want to change group sizes
1528 // define a new group here
1529 SequenceGroup sg = new SequenceGroup();
1530 sg.setStartRes(res);
1532 sg.addSequence(sequence, false);
1533 av.setSelectionGroup(sg);
1536 if (av.getConservationSelected())
1538 SliderPanel.setConservationSlider(ap, av.getGlobalColourScheme(),
1542 if (av.getAbovePIDThreshold())
1544 SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(),
1547 if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))
1549 // Edit end res position of selected group
1550 changeEndRes = true;
1552 else if ((stretchGroup != null)
1553 && (stretchGroup.getStartRes() == res))
1555 // Edit end res position of selected group
1556 changeStartRes = true;
1558 stretchGroup.getWidth();
1561 seqCanvas.repaint();
1570 public void doMouseReleasedDefineMode(MouseEvent evt)
1572 if (stretchGroup == null)
1577 if (stretchGroup.cs != null)
1579 if (stretchGroup.cs instanceof ClustalxColourScheme)
1581 ((ClustalxColourScheme) stretchGroup.cs).resetClustalX(stretchGroup
1582 .getSequences(av.hiddenRepSequences), stretchGroup
1586 if (stretchGroup.cs instanceof Blosum62ColourScheme
1587 || stretchGroup.cs instanceof PIDColourScheme
1588 || stretchGroup.cs.conservationApplied()
1589 || stretchGroup.cs.getThreshold() > 0)
1591 stretchGroup.recalcConservation();
1594 if (stretchGroup.cs.conservationApplied())
1596 SliderPanel.setConservationSlider(ap, stretchGroup.cs, stretchGroup
1601 SliderPanel.setPIDSliderSource(ap, stretchGroup.cs, stretchGroup
1604 PaintRefresher.Refresh(this, av.getSequenceSetId());
1605 ap.paintAlignment(true);
1608 changeEndRes = false;
1609 changeStartRes = false;
1610 stretchGroup = null;
1620 public void doMouseDraggedDefineMode(MouseEvent evt)
1622 int res = findRes(evt);
1623 int y = findSeq(evt);
1625 if (wrappedBlock != startWrapBlock)
1630 if (stretchGroup == null)
1635 if (res >= av.alignment.getWidth())
1637 res = av.alignment.getWidth() - 1;
1640 if (stretchGroup.getEndRes() == res)
1642 // Edit end res position of selected group
1643 changeEndRes = true;
1645 else if (stretchGroup.getStartRes() == res)
1647 // Edit start res position of selected group
1648 changeStartRes = true;
1651 if (res < av.getStartRes())
1653 res = av.getStartRes();
1658 if (res > (stretchGroup.getStartRes() - 1))
1660 stretchGroup.setEndRes(res);
1663 else if (changeStartRes)
1665 if (res < (stretchGroup.getEndRes() + 1))
1667 stretchGroup.setStartRes(res);
1671 int dragDirection = 0;
1677 else if (y < oldSeq)
1682 while ((y != oldSeq) && (oldSeq > -1) && (y < av.alignment.getHeight()))
1684 // This routine ensures we don't skip any sequences, as the
1685 // selection is quite slow.
1686 Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);
1688 oldSeq += dragDirection;
1695 Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);
1697 if (stretchGroup.getSequences(null).contains(nextSeq))
1699 stretchGroup.deleteSequence(seq, false);
1705 stretchGroup.addSequence(seq, false);
1708 stretchGroup.addSequence(nextSeq, false);
1717 mouseDragging = true;
1719 if (scrollThread != null)
1721 scrollThread.setEvent(evt);
1724 seqCanvas.repaint();
1727 void scrollCanvas(MouseEvent evt)
1731 if (scrollThread != null)
1733 scrollThread.running = false;
1734 scrollThread = null;
1736 mouseDragging = false;
1740 if (scrollThread == null)
1742 scrollThread = new ScrollThread();
1745 mouseDragging = true;
1746 scrollThread.setEvent(evt);
1751 // this class allows scrolling off the bottom of the visible alignment
1752 class ScrollThread extends Thread
1756 boolean running = false;
1758 public ScrollThread()
1763 public void setEvent(MouseEvent e)
1768 public void stopScrolling()
1781 if (mouseDragging && (evt.getY() < 0) && (av.getStartSeq() > 0))
1783 running = ap.scrollUp(true);
1786 if (mouseDragging && (evt.getY() >= getHeight())
1787 && (av.alignment.getHeight() > av.getEndSeq()))
1789 running = ap.scrollUp(false);
1792 if (mouseDragging && (evt.getX() < 0))
1794 running = ap.scrollRight(false);
1796 else if (mouseDragging && (evt.getX() >= getWidth()))
1798 running = ap.scrollRight(true);
1805 } catch (Exception ex)