2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
3 * Copyright (C) 2014 The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
11 * Jalview is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty
13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along with Jalview. If not, see <http://www.gnu.org/licenses/>.
17 * The Jalview Authors are detailed in the 'AUTHORS' file.
19 package jalview.appletgui;
24 import java.awt.event.*;
26 import jalview.commands.*;
27 import jalview.datamodel.*;
28 import jalview.schemes.*;
29 import jalview.structure.SelectionSource;
30 import jalview.structure.SequenceListener;
31 import jalview.structure.StructureSelectionManager;
33 public class SeqPanel extends Panel implements MouseMotionListener,
34 MouseListener, SequenceListener
37 public SeqCanvas seqCanvas;
39 public AlignmentPanel ap;
41 protected int lastres;
43 protected int startseq;
45 protected AlignViewport av;
47 // if character is inserted or deleted, we will need to recalculate the
49 boolean seqEditOccurred = false;
51 ScrollThread scrollThread = null;
53 boolean mouseDragging = false;
55 boolean editingSeqs = false;
57 boolean groupEditing = false;
61 boolean changeEndSeq = false;
63 boolean changeStartSeq = false;
65 boolean changeEndRes = false;
67 boolean changeStartRes = false;
69 SequenceGroup stretchGroup = null;
71 StringBuffer keyboardNo1;
73 StringBuffer keyboardNo2;
75 boolean mouseWheelPressed = false;
79 EditCommand editCommand;
81 StructureSelectionManager ssm;
83 public SeqPanel(AlignViewport avp, AlignmentPanel p)
87 seqCanvas = new SeqCanvas(avp);
88 setLayout(new BorderLayout());
93 seqCanvas.addMouseMotionListener(this);
94 seqCanvas.addMouseListener(this);
95 ssm = StructureSelectionManager.getStructureSelectionManager(av.applet);
96 ssm.addStructureViewerListener(this);
103 if (editCommand != null && editCommand.getSize() > 0)
105 ap.alignFrame.addHistoryItem(editCommand);
106 av.firePropertyChange("alignment", null, av.getAlignment()
113 groupEditing = false;
121 seqCanvas.cursorY = getKeyboardNo1() - 1;
125 void setCursorColumn()
127 seqCanvas.cursorX = getKeyboardNo1() - 1;
131 void setCursorRowAndColumn()
133 if (keyboardNo2 == null)
135 keyboardNo2 = new StringBuffer();
139 seqCanvas.cursorX = getKeyboardNo1() - 1;
140 seqCanvas.cursorY = getKeyboardNo2() - 1;
145 void setCursorPosition()
147 SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(
150 seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1;
154 void moveCursor(int dx, int dy)
156 seqCanvas.cursorX += dx;
157 seqCanvas.cursorY += dy;
158 if (av.hasHiddenColumns()
159 && !av.getColumnSelection().isVisible(seqCanvas.cursorX))
161 int original = seqCanvas.cursorX - dx;
162 int maxWidth = av.getAlignment().getWidth();
164 while (!av.getColumnSelection().isVisible(seqCanvas.cursorX)
165 && seqCanvas.cursorX < maxWidth && seqCanvas.cursorX > 0)
167 seqCanvas.cursorX += dx;
170 if (seqCanvas.cursorX >= maxWidth
171 || !av.getColumnSelection().isVisible(seqCanvas.cursorX))
173 seqCanvas.cursorX = original;
179 void scrollToVisible()
181 if (seqCanvas.cursorX < 0)
183 seqCanvas.cursorX = 0;
185 else if (seqCanvas.cursorX > av.getAlignment().getWidth() - 1)
187 seqCanvas.cursorX = av.getAlignment().getWidth() - 1;
190 if (seqCanvas.cursorY < 0)
192 seqCanvas.cursorY = 0;
194 else if (seqCanvas.cursorY > av.getAlignment().getHeight() - 1)
196 seqCanvas.cursorY = av.getAlignment().getHeight() - 1;
200 if (av.wrapAlignment)
202 ap.scrollToWrappedVisible(seqCanvas.cursorX);
206 while (seqCanvas.cursorY < av.startSeq)
210 while (seqCanvas.cursorY + 1 > av.endSeq)
214 while (seqCanvas.cursorX < av.getColumnSelection()
215 .adjustForHiddenColumns(av.startRes))
218 if (!ap.scrollRight(false))
223 while (seqCanvas.cursorX > av.getColumnSelection()
224 .adjustForHiddenColumns(av.endRes))
226 if (!ap.scrollRight(true))
232 setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
233 seqCanvas.cursorX, seqCanvas.cursorY);
238 void setSelectionAreaAtCursor(boolean topLeft)
240 SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(
243 if (av.getSelectionGroup() != null)
245 SequenceGroup sg = av.getSelectionGroup();
246 // Find the top and bottom of this group
247 int min = av.getAlignment().getHeight(), max = 0;
248 for (int i = 0; i < sg.getSize(); i++)
250 int index = av.getAlignment().findIndex(sg.getSequenceAt(i));
265 sg.setStartRes(seqCanvas.cursorX);
266 if (sg.getEndRes() < seqCanvas.cursorX)
268 sg.setEndRes(seqCanvas.cursorX);
271 min = seqCanvas.cursorY;
275 sg.setEndRes(seqCanvas.cursorX);
276 if (sg.getStartRes() > seqCanvas.cursorX)
278 sg.setStartRes(seqCanvas.cursorX);
281 max = seqCanvas.cursorY + 1;
286 // Only the user can do this
287 av.setSelectionGroup(null);
291 // Now add any sequences between min and max
293 for (int i = min; i < max; i++)
295 sg.addSequence(av.getAlignment().getSequenceAt(i), false);
300 if (av.getSelectionGroup() == null)
302 SequenceGroup sg = new SequenceGroup();
303 sg.setStartRes(seqCanvas.cursorX);
304 sg.setEndRes(seqCanvas.cursorX);
305 sg.addSequence(sequence, false);
306 av.setSelectionGroup(sg);
308 ap.paintAlignment(false);
312 void insertGapAtCursor(boolean group)
314 groupEditing = group;
315 startseq = seqCanvas.cursorY;
316 lastres = seqCanvas.cursorX;
317 editSequence(true, seqCanvas.cursorX + getKeyboardNo1());
321 void deleteGapAtCursor(boolean group)
323 groupEditing = group;
324 startseq = seqCanvas.cursorY;
325 lastres = seqCanvas.cursorX + getKeyboardNo1();
326 editSequence(false, seqCanvas.cursorX);
330 void numberPressed(char value)
332 if (keyboardNo1 == null)
334 keyboardNo1 = new StringBuffer();
337 if (keyboardNo2 != null)
339 keyboardNo2.append(value);
343 keyboardNo1.append(value);
349 if (keyboardNo1 != null)
351 int value = Integer.parseInt(keyboardNo1.toString());
355 } catch (Exception x)
364 if (keyboardNo2!=null){
365 int value = Integer.parseInt(keyboardNo2.toString());
369 } catch (Exception x)
375 void setStatusMessage(SequenceI sequence, int res, int seq)
377 StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: "
378 + sequence.getName());
381 if (av.getAlignment().isNucleotide())
383 obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)
387 text.append(" Nucleotide: ");
392 obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
395 text.append(" Residue: ");
404 text.append(obj + " (" + sequence.findPosition(res) + ")");
408 ap.alignFrame.statusBar.setText(text.toString());
412 public void mousePressed(MouseEvent evt)
414 lastMousePress = evt.getPoint();
416 // For now, ignore the mouseWheel font resizing on Macs
417 // As the Button2_mask always seems to be true
418 if ((evt.getModifiers() & InputEvent.BUTTON2_MASK) == InputEvent.BUTTON2_MASK
421 mouseWheelPressed = true;
425 if (evt.isShiftDown() || evt.isControlDown() || evt.isAltDown())
427 if (evt.isControlDown() || evt.isAltDown())
435 doMousePressedDefineMode(evt);
439 int seq = findSeq(evt);
440 int res = findRes(evt);
442 if (seq < 0 || res < 0)
447 if ((seq < av.getAlignment().getHeight())
448 && (res < av.getAlignment().getSequenceAt(seq).getLength()))
462 public void mouseClicked(MouseEvent evt)
464 SequenceI sequence = av.getAlignment().getSequenceAt(findSeq(evt));
465 if (evt.getClickCount() > 1)
467 if (av.getSelectionGroup() != null
468 && av.getSelectionGroup().getSize() == 1
469 && av.getSelectionGroup().getEndRes()
470 - av.getSelectionGroup().getStartRes() < 2)
472 av.setSelectionGroup(null);
475 SequenceFeature[] features = findFeaturesAtRes(sequence,
476 sequence.findPosition(findRes(evt)));
478 if (features != null && features.length > 0)
480 SearchResults highlight = new SearchResults();
481 highlight.addResult(sequence, features[0].getBegin(),
482 features[0].getEnd());
483 seqCanvas.highlightSearchResults(highlight);
485 if (features != null && features.length > 0)
487 seqCanvas.getFeatureRenderer().amendFeatures(new SequenceI[]
488 { sequence }, features, false, ap);
490 seqCanvas.highlightSearchResults(null);
495 public void mouseReleased(MouseEvent evt)
497 mouseDragging = false;
498 mouseWheelPressed = false;
499 ap.paintAlignment(true);
503 doMouseReleasedDefineMode(evt);
511 int startWrapBlock = -1;
513 int wrappedBlock = -1;
515 int findRes(MouseEvent evt)
520 if (av.wrapAlignment)
523 int hgap = av.charHeight;
524 if (av.scaleAboveWrapped)
526 hgap += av.charHeight;
529 int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
530 + seqCanvas.getAnnotationHeight();
534 x -= seqCanvas.LABEL_WEST;
536 int cwidth = seqCanvas.getWrappedCanvasWidth(getSize().width);
542 wrappedBlock = y / cHeight;
543 wrappedBlock += av.getStartRes() / cwidth;
545 res = wrappedBlock * cwidth + x / av.getCharWidth();
550 res = (x / av.getCharWidth()) + av.getStartRes();
553 if (av.hasHiddenColumns())
555 res = av.getColumnSelection().adjustForHiddenColumns(res);
562 int findSeq(MouseEvent evt)
564 final int sqnum = findAlRow(evt);
565 return (sqnum < 0) ? 0 : sqnum;
571 * @return row in alignment that was selected (or -1 for column selection)
573 private int findAlRow(MouseEvent evt)
578 if (av.wrapAlignment)
580 int hgap = av.charHeight;
581 if (av.scaleAboveWrapped)
583 hgap += av.charHeight;
586 int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
587 + seqCanvas.getAnnotationHeight();
591 seq = Math.min((y % cHeight) / av.getCharHeight(), av.getAlignment()
600 seq = Math.min((y / av.getCharHeight()) + av.getStartSeq(), av
601 .getAlignment().getHeight() - 1);
611 public void doMousePressed(MouseEvent evt)
614 int seq = findSeq(evt);
615 int res = findRes(evt);
617 if (seq < av.getAlignment().getHeight()
618 && res < av.getAlignment().getSequenceAt(seq).getLength())
620 // char resstr = align.getSequenceAt(seq).getSequence().charAt(res);
621 // Find the residue's position in the sequence (res is the position
638 public void mouseOverSequence(SequenceI sequence, int index, int pos)
640 String tmp = sequence.hashCode() + index + "";
641 if (lastMessage == null || !lastMessage.equals(tmp))
642 ssm.mouseOverSequence(sequence, index, pos, av);
647 public void highlightSequence(SearchResults results)
649 if (av.followHighlight)
651 if (ap.scrollToPosition(results, true))
653 ap.alignFrame.repaint();
656 seqCanvas.highlightSearchResults(results);
660 public void updateColours(SequenceI seq, int index)
662 System.out.println("update the seqPanel colours");
666 public void mouseMoved(MouseEvent evt)
668 int res = findRes(evt);
669 int seq = findSeq(evt);
671 if (seq >= av.getAlignment().getHeight() || seq < 0 || res < 0)
680 SequenceI sequence = av.getAlignment().getSequenceAt(seq);
681 if (res > sequence.getLength())
690 int respos = sequence.findPosition(res);
692 mouseOverSequence(sequence, res, respos);
694 StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: "
695 + sequence.getName());
698 if (av.getAlignment().isNucleotide())
700 obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)
704 text.append(" Nucleotide: ");
709 obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
712 text.append(" Residue: ");
720 text.append(obj + " (" + respos + ")");
724 ap.alignFrame.statusBar.setText(text.toString());
726 StringBuffer tooltipText = new StringBuffer();
727 SequenceGroup[] groups = av.getAlignment().findAllGroups(sequence);
730 for (int g = 0; g < groups.length; g++)
732 if (groups[g].getStartRes() <= res && groups[g].getEndRes() >= res)
734 if (!groups[g].getName().startsWith("JTreeGroup")
735 && !groups[g].getName().startsWith("JGroup"))
737 tooltipText.append(groups[g].getName() + " ");
739 if (groups[g].getDescription() != null)
741 tooltipText.append(groups[g].getDescription());
743 tooltipText.append("\n");
748 // use aa to see if the mouse pointer is on a
749 SequenceFeature[] allFeatures = findFeaturesAtRes(sequence,
750 sequence.findPosition(res));
753 while (index < allFeatures.length)
755 SequenceFeature sf = allFeatures[index];
757 tooltipText.append(sf.getType() + " " + sf.begin + ":" + sf.end);
759 if (sf.getDescription() != null)
761 tooltipText.append(" " + sf.getDescription());
764 if (sf.getValue("status") != null)
766 String status = sf.getValue("status").toString();
767 if (status.length() > 0)
769 tooltipText.append(" (" + sf.getValue("status") + ")");
772 tooltipText.append("\n");
779 tooltip = new Tooltip(tooltipText.toString(), seqCanvas);
783 tooltip.setTip(tooltipText.toString());
787 SequenceFeature[] findFeaturesAtRes(SequenceI sequence, int res)
789 Vector tmp = new Vector();
790 SequenceFeature[] features = sequence.getSequenceFeatures();
791 if (features != null)
793 for (int i = 0; i < features.length; i++)
795 if (av.featuresDisplayed == null
796 || !av.featuresDisplayed.containsKey(features[i].getType()))
801 if (features[i].featureGroup != null
802 && seqCanvas.fr.featureGroups != null
803 && seqCanvas.fr.featureGroups
804 .containsKey(features[i].featureGroup)
805 && !((Boolean) seqCanvas.fr.featureGroups
806 .get(features[i].featureGroup)).booleanValue())
809 if ((features[i].getBegin() <= res)
810 && (features[i].getEnd() >= res))
812 tmp.addElement(features[i]);
817 features = new SequenceFeature[tmp.size()];
818 tmp.copyInto(features);
825 public void mouseDragged(MouseEvent evt)
827 if (mouseWheelPressed)
829 int oldWidth = av.charWidth;
831 // Which is bigger, left-right or up-down?
832 if (Math.abs(evt.getY() - lastMousePress.y) > Math.abs(evt.getX()
835 int fontSize = av.font.getSize();
837 if (evt.getY() < lastMousePress.y && av.charHeight > 1)
841 else if (evt.getY() > lastMousePress.y)
851 av.setFont(new Font(av.font.getName(), av.font.getStyle(), fontSize));
852 av.charWidth = oldWidth;
856 if (evt.getX() < lastMousePress.x && av.charWidth > 1)
860 else if (evt.getX() > lastMousePress.x)
865 if (av.charWidth < 1)
873 FontMetrics fm = getFontMetrics(av.getFont());
874 av.validCharWidth = fm.charWidth('M') <= av.charWidth;
876 lastMousePress = evt.getPoint();
878 ap.paintAlignment(false);
879 ap.annotationPanel.image = null;
885 doMouseDraggedDefineMode(evt);
889 int res = findRes(evt);
896 if ((lastres == -1) || (lastres == res))
901 if ((res < av.getAlignment().getWidth()) && (res < lastres))
903 // dragLeft, delete gap
904 editSequence(false, res);
908 editSequence(true, res);
911 mouseDragging = true;
912 if (scrollThread != null)
914 scrollThread.setEvent(evt);
919 synchronized void editSequence(boolean insertGap, int startres)
923 boolean fixedColumns = false;
924 SequenceGroup sg = av.getSelectionGroup();
926 SequenceI seq = av.getAlignment().getSequenceAt(startseq);
928 if (!groupEditing && av.hasHiddenRows())
930 if (av.isHiddenRepSequence(seq))
932 sg = (SequenceGroup) av.getRepresentedSequences(seq);
937 StringBuffer message = new StringBuffer();
940 message.append("Edit group:");
941 if (editCommand == null)
943 editCommand = new EditCommand("Edit Group");
948 message.append("Edit sequence: " + seq.getName());
949 String label = seq.getName();
950 if (label.length() > 10)
952 label = label.substring(0, 10);
954 if (editCommand == null)
956 editCommand = new EditCommand("Edit " + label);
962 message.append(" insert ");
966 message.append(" delete ");
969 message.append(Math.abs(startres - lastres) + " gaps.");
970 ap.alignFrame.statusBar.setText(message.toString());
972 // Are we editing within a selection group?
974 || (sg != null && sg.getSequences(av.getHiddenRepSequences())
979 // sg might be null as the user may only see 1 sequence,
980 // but the sequence represents a group
983 if (!av.isHiddenRepSequence(seq))
989 sg = av.getRepresentedSequences(seq);
992 fixedLeft = sg.getStartRes();
993 fixedRight = sg.getEndRes();
995 if ((startres < fixedLeft && lastres >= fixedLeft)
996 || (startres >= fixedLeft && lastres < fixedLeft)
997 || (startres > fixedRight && lastres <= fixedRight)
998 || (startres <= fixedRight && lastres > fixedRight))
1004 if (fixedLeft > startres)
1006 fixedRight = fixedLeft - 1;
1009 else if (fixedRight < startres)
1011 fixedLeft = fixedRight;
1016 if (av.hasHiddenColumns())
1018 fixedColumns = true;
1019 int y1 = av.getColumnSelection().getHiddenBoundaryLeft(startres);
1020 int y2 = av.getColumnSelection().getHiddenBoundaryRight(startres);
1022 if ((insertGap && startres > y1 && lastres < y1)
1023 || (!insertGap && startres < y2 && lastres > y2))
1029 // System.out.print(y1+" "+y2+" "+fixedLeft+" "+fixedRight+"~~");
1030 // Selection spans a hidden region
1031 if (fixedLeft < y1 && (fixedRight > y2 || fixedRight == -1))
1039 fixedRight = y2 - 1;
1046 SequenceI[] groupSeqs = sg.getSequences(av.getHiddenRepSequences())
1047 .toArray(new SequenceI[0]);
1052 // If the user has selected the whole sequence, and is dragging to
1053 // the right, we can still extend the alignment and selectionGroup
1054 if (sg.getStartRes() == 0 && sg.getEndRes() == fixedRight
1055 && sg.getEndRes() == av.getAlignment().getWidth() - 1)
1057 sg.setEndRes(av.getAlignment().getWidth() + startres - lastres);
1058 fixedRight = sg.getEndRes();
1061 // Is it valid with fixed columns??
1062 // Find the next gap before the end
1063 // of the visible region boundary
1064 boolean blank = false;
1065 for (fixedRight = fixedRight; fixedRight > lastres; fixedRight--)
1069 for (SequenceI gs : groupSeqs)
1071 for (int j = 0; j < startres - lastres; j++)
1073 if (!jalview.util.Comparison.isGap(gs.getCharAt(fixedRight
1089 if (sg.getSize() == av.getAlignment().getHeight())
1091 if ((av.hasHiddenColumns() && startres < av
1092 .getColumnSelection().getHiddenBoundaryRight(startres)))
1098 int alWidth = av.getAlignment().getWidth();
1099 if (av.hasHiddenRows())
1101 int hwidth = av.getAlignment().getHiddenSequences()
1103 if (hwidth > alWidth)
1108 // We can still insert gaps if the selectionGroup
1109 // contains all the sequences
1110 sg.setEndRes(sg.getEndRes() + startres - lastres);
1111 fixedRight = alWidth + startres - lastres;
1122 else if (!insertGap)
1124 // / Are we able to delete?
1125 // ie are all columns blank?
1127 for (SequenceI gs : groupSeqs)
1129 for (int j = startres; j < lastres; j++)
1131 if (gs.getLength() <= j)
1136 if (!jalview.util.Comparison.isGap(gs.getCharAt(j)))
1138 // Not a gap, block edit not valid
1148 // dragging to the right
1149 if (fixedColumns && fixedRight != -1)
1151 for (int j = lastres; j < startres; j++)
1153 insertChar(j, groupSeqs, fixedRight);
1158 editCommand.appendEdit(EditCommand.INSERT_GAP, groupSeqs,
1159 startres, startres - lastres, av.getAlignment(), true);
1164 // dragging to the left
1165 if (fixedColumns && fixedRight != -1)
1167 for (int j = lastres; j > startres; j--)
1169 deleteChar(startres, groupSeqs, fixedRight);
1174 editCommand.appendEdit(EditCommand.DELETE_GAP, groupSeqs,
1175 startres, lastres - startres, av.getAlignment(), true);
1181 // ///Editing a single sequence///////////
1185 // dragging to the right
1186 if (fixedColumns && fixedRight != -1)
1188 for (int j = lastres; j < startres; j++)
1190 insertChar(j, new SequenceI[]
1191 { seq }, fixedRight);
1196 editCommand.appendEdit(EditCommand.INSERT_GAP, new SequenceI[]
1197 { seq }, lastres, startres - lastres, av.getAlignment(), true);
1202 // dragging to the left
1203 if (fixedColumns && fixedRight != -1)
1205 for (int j = lastres; j > startres; j--)
1207 if (!jalview.util.Comparison.isGap(seq.getCharAt(startres)))
1212 deleteChar(startres, new SequenceI[]
1213 { seq }, fixedRight);
1218 // could be a keyboard edit trying to delete none gaps
1220 for (int m = startres; m < lastres; m++)
1222 if (!jalview.util.Comparison.isGap(seq.getCharAt(m)))
1231 editCommand.appendEdit(EditCommand.DELETE_GAP, new SequenceI[]
1232 { seq }, startres, max, av.getAlignment(), true);
1239 seqCanvas.repaint();
1242 void insertChar(int j, SequenceI[] seq, int fixedColumn)
1244 int blankColumn = fixedColumn;
1245 for (int s = 0; s < seq.length; s++)
1247 // Find the next gap before the end of the visible region boundary
1248 // If lastCol > j, theres a boundary after the gap insertion
1250 for (blankColumn = fixedColumn; blankColumn > j; blankColumn--)
1252 if (jalview.util.Comparison.isGap(seq[s].getCharAt(blankColumn)))
1254 // Theres a space, so break and insert the gap
1259 if (blankColumn <= j)
1261 blankColumn = fixedColumn;
1267 editCommand.appendEdit(EditCommand.DELETE_GAP, seq, blankColumn, 1,
1268 av.getAlignment(), true);
1270 editCommand.appendEdit(EditCommand.INSERT_GAP, seq, j, 1,
1271 av.getAlignment(), true);
1275 void deleteChar(int j, SequenceI[] seq, int fixedColumn)
1278 editCommand.appendEdit(EditCommand.DELETE_GAP, seq, j, 1,
1279 av.getAlignment(), true);
1281 editCommand.appendEdit(EditCommand.INSERT_GAP, seq, fixedColumn, 1,
1282 av.getAlignment(), true);
1285 // ////////////////////////////////////////
1286 // ///Everything below this is for defining the boundary of the rubberband
1287 // ////////////////////////////////////////
1288 public void doMousePressedDefineMode(MouseEvent evt)
1290 if (scrollThread != null)
1292 scrollThread.running = false;
1293 scrollThread = null;
1296 int res = findRes(evt);
1297 int seq = findSeq(evt);
1299 startWrapBlock = wrappedBlock;
1306 SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seq);
1308 if (sequence == null || res > sequence.getLength())
1313 stretchGroup = av.getSelectionGroup();
1315 if (stretchGroup == null)
1317 stretchGroup = av.getAlignment().findGroup(sequence);
1318 if (stretchGroup != null && res > stretchGroup.getStartRes()
1319 && res < stretchGroup.getEndRes())
1321 av.setSelectionGroup(stretchGroup);
1325 stretchGroup = null;
1329 else if (!stretchGroup.getSequences(null).contains(sequence)
1330 || stretchGroup.getStartRes() > res
1331 || stretchGroup.getEndRes() < res)
1333 stretchGroup = null;
1335 SequenceGroup[] allGroups = av.getAlignment().findAllGroups(sequence);
1337 if (allGroups != null)
1339 for (int i = 0; i < allGroups.length; i++)
1341 if (allGroups[i].getStartRes() <= res
1342 && allGroups[i].getEndRes() >= res)
1344 stretchGroup = allGroups[i];
1349 av.setSelectionGroup(stretchGroup);
1352 // DETECT RIGHT MOUSE BUTTON IN AWT
1353 if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
1355 SequenceFeature[] allFeatures = findFeaturesAtRes(sequence,
1356 sequence.findPosition(res));
1358 Vector links = null;
1359 if (allFeatures != null)
1361 for (int i = 0; i < allFeatures.length; i++)
1363 if (allFeatures[i].links != null)
1367 links = new Vector();
1369 for (int j = 0; j < allFeatures[i].links.size(); j++)
1371 links.addElement(allFeatures[i].links.elementAt(j));
1376 APopupMenu popup = new APopupMenu(ap, null, links);
1378 popup.show(this, evt.getX(), evt.getY());
1384 seqCanvas.cursorX = findRes(evt);
1385 seqCanvas.cursorY = findSeq(evt);
1386 seqCanvas.repaint();
1390 // Only if left mouse button do we want to change group sizes
1392 if (stretchGroup == null)
1394 // define a new group here
1395 SequenceGroup sg = new SequenceGroup();
1396 sg.setStartRes(res);
1398 sg.addSequence(sequence, false);
1399 av.setSelectionGroup(sg);
1402 if (av.getConservationSelected())
1404 SliderPanel.setConservationSlider(ap, av.getGlobalColourScheme(),
1407 if (av.getAbovePIDThreshold())
1409 SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(),
1416 public void doMouseReleasedDefineMode(MouseEvent evt)
1418 if (stretchGroup == null)
1423 stretchGroup.recalcConservation(); // always do this - annotation has own
1425 if (stretchGroup.cs != null)
1427 stretchGroup.cs.alignmentChanged(stretchGroup,
1428 av.getHiddenRepSequences());
1430 if (stretchGroup.cs.conservationApplied())
1432 SliderPanel.setConservationSlider(ap, stretchGroup.cs,
1433 stretchGroup.getName());
1437 SliderPanel.setPIDSliderSource(ap, stretchGroup.cs,
1438 stretchGroup.getName());
1441 changeEndRes = false;
1442 changeStartRes = false;
1443 stretchGroup = null;
1444 PaintRefresher.Refresh(ap, av.getSequenceSetId());
1445 ap.paintAlignment(true);
1449 public void doMouseDraggedDefineMode(MouseEvent evt)
1451 int res = findRes(evt);
1452 int y = findSeq(evt);
1454 if (wrappedBlock != startWrapBlock)
1459 if (stretchGroup == null)
1464 mouseDragging = true;
1466 if (y > av.getAlignment().getHeight())
1468 y = av.getAlignment().getHeight() - 1;
1471 if (res >= av.getAlignment().getWidth())
1473 res = av.getAlignment().getWidth() - 1;
1476 if (stretchGroup.getEndRes() == res)
1478 // Edit end res position of selected group
1479 changeEndRes = true;
1481 else if (stretchGroup.getStartRes() == res)
1483 // Edit start res position of selected group
1484 changeStartRes = true;
1494 if (res > (stretchGroup.getStartRes() - 1))
1496 stretchGroup.setEndRes(res);
1499 else if (changeStartRes)
1501 if (res < (stretchGroup.getEndRes() + 1))
1503 stretchGroup.setStartRes(res);
1507 int dragDirection = 0;
1513 else if (y < oldSeq)
1518 while ((y != oldSeq) && (oldSeq > -1)
1519 && (y < av.getAlignment().getHeight()))
1521 // This routine ensures we don't skip any sequences, as the
1522 // selection is quite slow.
1523 Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);
1525 oldSeq += dragDirection;
1532 Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);
1534 if (stretchGroup.getSequences(null).contains(nextSeq))
1536 stretchGroup.deleteSequence(seq, false);
1542 stretchGroup.addSequence(seq, false);
1545 stretchGroup.addSequence(nextSeq, false);
1554 if (res > av.endRes || res < av.startRes || y < av.startSeq
1560 if (scrollThread != null)
1562 scrollThread.setEvent(evt);
1565 seqCanvas.repaint();
1568 public void mouseEntered(MouseEvent e)
1575 if (scrollThread != null)
1577 scrollThread.running = false;
1578 scrollThread = null;
1582 public void mouseExited(MouseEvent e)
1584 if (av.getWrapAlignment())
1589 if (mouseDragging && scrollThread == null)
1591 scrollThread = new ScrollThread();
1595 void scrollCanvas(MouseEvent evt)
1599 if (scrollThread != null)
1601 scrollThread.running = false;
1602 scrollThread = null;
1604 mouseDragging = false;
1608 if (scrollThread == null)
1610 scrollThread = new ScrollThread();
1613 mouseDragging = true;
1614 scrollThread.setEvent(evt);
1619 // this class allows scrolling off the bottom of the visible alignment
1620 class ScrollThread extends Thread
1624 boolean running = false;
1626 public ScrollThread()
1631 public void setEvent(MouseEvent e)
1636 public void stopScrolling()
1650 if (mouseDragging && evt.getY() < 0 && av.getStartSeq() > 0)
1652 running = ap.scrollUp(true);
1655 if (mouseDragging && evt.getY() >= getSize().height
1656 && av.getAlignment().getHeight() > av.getEndSeq())
1658 running = ap.scrollUp(false);
1661 if (mouseDragging && evt.getX() < 0)
1663 running = ap.scrollRight(false);
1666 else if (mouseDragging && evt.getX() >= getSize().width)
1668 running = ap.scrollRight(true);
1675 } catch (Exception ex)
1683 * modify current selection according to a received message.
1685 public void selection(SequenceGroup seqsel, ColumnSelection colsel,
1686 SelectionSource source)
1688 // TODO: fix this hack - source of messages is align viewport, but SeqPanel
1689 // handles selection messages...
1690 // TODO: extend config options to allow user to control if selections may be
1691 // shared between viewports.
1693 && (av == source || !av.followSelection || (source instanceof AlignViewport && ((AlignViewport) source)
1694 .getSequenceSetId().equals(av.getSequenceSetId()))))
1698 // do we want to thread this ? (contention with seqsel and colsel locks, I
1700 // rules are: colsel is copied if there is a real intersection between
1701 // sequence selection
1702 boolean repaint = false, copycolsel = true;
1703 if (av.getSelectionGroup() == null || !av.isSelectionGroupChanged(true))
1705 SequenceGroup sgroup = null;
1706 if (seqsel != null && seqsel.getSize() > 0)
1708 if (av.getAlignment() == null)
1711 .println("Selection message: alignviewport av SeqSetId="
1712 + av.getSequenceSetId() + " ViewId="
1714 + " 's alignment is NULL! returning immediatly.");
1717 sgroup = seqsel.intersect(av.getAlignment(),
1718 (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null);
1719 if ((sgroup == null || sgroup.getSize() == 0)
1720 && (colsel == null || colsel.size() == 0))
1722 // don't copy columns if the region didn't intersect.
1726 if (sgroup != null && sgroup.getSize() > 0)
1728 av.setSelectionGroup(sgroup);
1732 av.setSelectionGroup(null);
1734 repaint = av.isSelectionGroupChanged(true);
1737 && (av.getColumnSelection() == null || !av
1738 .isColSelChanged(true)))
1740 // the current selection is unset or from a previous message
1741 // so import the new colsel.
1742 if (colsel == null || colsel.size() == 0)
1744 if (av.getColumnSelection() != null)
1746 av.getColumnSelection().clear();
1751 // TODO: shift colSel according to the intersecting sequences
1752 if (av.getColumnSelection() == null)
1754 av.setColumnSelection(new ColumnSelection(colsel));
1758 av.getColumnSelection().setElementsFrom(colsel);
1761 repaint |= av.isColSelChanged(true);
1764 && av.hasHiddenColumns()
1765 && (av.getColumnSelection() == null || av.getColumnSelection()
1766 .getHiddenColumns() == null))
1768 System.err.println("Bad things");
1772 ap.scalePanelHolder.repaint();
1778 * scroll to the given row/column - or nearest visible location
1783 public void scrollTo(int row, int column)
1786 row = row < 0 ? ap.av.startSeq : row;
1787 column = column < 0 ? ap.av.startRes : column;
1788 ap.scrollTo(column, column, row, true, true);
1792 * scroll to the given row - or nearest visible location
1796 public void scrollToRow(int row)
1799 row = row < 0 ? ap.av.startSeq : row;
1800 ap.scrollTo(ap.av.startRes, ap.av.startRes, row, true, true);
1804 * scroll to the given column - or nearest visible location
1808 public void scrollToColumn(int column)
1811 column = column < 0 ? ap.av.startRes : column;
1812 ap.scrollTo(column, column, ap.av.startSeq, true, true);