2 * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
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
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
24 import java.util.List;
27 import java.awt.event.*;
31 import jalview.commands.*;
32 import jalview.datamodel.*;
33 import jalview.io.SequenceAnnotationReport;
34 import jalview.schemes.*;
35 import jalview.structure.*;
36 import jalview.util.MessageManager;
42 * @version $Revision: 1.130 $
44 public class SeqPanel extends JPanel implements MouseListener,
45 MouseMotionListener, MouseWheelListener, SequenceListener,
50 public SeqCanvas seqCanvas;
53 public AlignmentPanel ap;
55 protected int lastres;
57 protected int startseq;
59 protected AlignViewport av;
61 ScrollThread scrollThread = null;
63 boolean mouseDragging = false;
65 boolean editingSeqs = false;
67 boolean groupEditing = false;
69 // ////////////////////////////////////////
70 // ///Everything below this is for defining the boundary of the rubberband
71 // ////////////////////////////////////////
74 boolean changeEndSeq = false;
76 boolean changeStartSeq = false;
78 boolean changeEndRes = false;
80 boolean changeStartRes = false;
82 SequenceGroup stretchGroup = null;
84 boolean remove = false;
88 boolean mouseWheelPressed = false;
90 StringBuffer keyboardNo1;
92 StringBuffer keyboardNo2;
94 java.net.URL linkImageURL;
96 private final SequenceAnnotationReport seqARep;
98 StringBuffer tooltipText = new StringBuffer("<html>");
102 EditCommand editCommand;
104 StructureSelectionManager ssm;
107 * Creates a new SeqPanel object.
114 public SeqPanel(AlignViewport av, AlignmentPanel ap)
116 linkImageURL = getClass().getResource("/images/link.gif");
117 seqARep = new SequenceAnnotationReport(linkImageURL.toString());
118 ToolTipManager.sharedInstance().registerComponent(this);
119 ToolTipManager.sharedInstance().setInitialDelay(0);
120 ToolTipManager.sharedInstance().setDismissDelay(10000);
122 setBackground(Color.white);
124 seqCanvas = new SeqCanvas(ap);
125 setLayout(new BorderLayout());
126 add(seqCanvas, BorderLayout.CENTER);
132 addMouseMotionListener(this);
133 addMouseListener(this);
134 addMouseWheelListener(this);
135 ssm = av.getStructureSelectionManager();
136 ssm.addStructureViewerListener(this);
137 ssm.addSelectionListener(this);
141 int startWrapBlock = -1;
143 int wrappedBlock = -1;
145 int findRes(MouseEvent evt)
150 if (av.wrapAlignment)
153 int hgap = av.charHeight;
154 if (av.scaleAboveWrapped)
156 hgap += av.charHeight;
159 int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
160 + seqCanvas.getAnnotationHeight();
164 x -= seqCanvas.LABEL_WEST;
166 int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());
172 wrappedBlock = y / cHeight;
173 wrappedBlock += av.getStartRes() / cwidth;
175 res = wrappedBlock * cwidth + x / av.getCharWidth();
180 if (x > seqCanvas.getWidth() + seqCanvas.getWidth())
182 // make sure we calculate relative to visible alignment, rather than
184 x = seqCanvas.getX() + seqCanvas.getWidth();
186 res = (x / av.getCharWidth()) + av.getStartRes();
189 if (av.hasHiddenColumns())
191 res = av.getColumnSelection().adjustForHiddenColumns(res);
198 int findSeq(MouseEvent evt)
203 if (av.wrapAlignment)
205 int hgap = av.charHeight;
206 if (av.scaleAboveWrapped)
208 hgap += av.charHeight;
211 int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
212 + seqCanvas.getAnnotationHeight();
216 seq = Math.min((y % cHeight) / av.getCharHeight(), av.getAlignment()
221 seq = Math.min((y / av.getCharHeight()) + av.getStartSeq(), av
222 .getAlignment().getHeight() - 1);
230 if (editCommand != null && editCommand.getSize() > 0)
232 ap.alignFrame.addHistoryItem(editCommand);
233 av.firePropertyChange("alignment", null, av.getAlignment()
240 groupEditing = false;
248 seqCanvas.cursorY = getKeyboardNo1() - 1;
252 void setCursorColumn()
254 seqCanvas.cursorX = getKeyboardNo1() - 1;
258 void setCursorRowAndColumn()
260 if (keyboardNo2 == null)
262 keyboardNo2 = new StringBuffer();
266 seqCanvas.cursorX = getKeyboardNo1() - 1;
267 seqCanvas.cursorY = getKeyboardNo2() - 1;
272 void setCursorPosition()
274 SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
276 seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1;
280 void moveCursor(int dx, int dy)
282 seqCanvas.cursorX += dx;
283 seqCanvas.cursorY += dy;
284 if (av.hasHiddenColumns()
285 && !av.getColumnSelection().isVisible(seqCanvas.cursorX))
287 int original = seqCanvas.cursorX - dx;
288 int maxWidth = av.getAlignment().getWidth();
290 while (!av.getColumnSelection().isVisible(seqCanvas.cursorX)
291 && seqCanvas.cursorX < maxWidth && seqCanvas.cursorX > 0)
293 seqCanvas.cursorX += dx;
296 if (seqCanvas.cursorX >= maxWidth
297 || !av.getColumnSelection().isVisible(seqCanvas.cursorX))
299 seqCanvas.cursorX = original;
306 void scrollToVisible()
308 if (seqCanvas.cursorX < 0)
310 seqCanvas.cursorX = 0;
312 else if (seqCanvas.cursorX > av.getAlignment().getWidth() - 1)
314 seqCanvas.cursorX = av.getAlignment().getWidth() - 1;
317 if (seqCanvas.cursorY < 0)
319 seqCanvas.cursorY = 0;
321 else if (seqCanvas.cursorY > av.getAlignment().getHeight() - 1)
323 seqCanvas.cursorY = av.getAlignment().getHeight() - 1;
327 if (av.wrapAlignment)
329 ap.scrollToWrappedVisible(seqCanvas.cursorX);
333 while (seqCanvas.cursorY < av.startSeq)
337 while (seqCanvas.cursorY + 1 > av.endSeq)
341 if (!av.wrapAlignment)
343 while (seqCanvas.cursorX < av.getColumnSelection()
344 .adjustForHiddenColumns(av.startRes))
346 if (!ap.scrollRight(false))
351 while (seqCanvas.cursorX > av.getColumnSelection()
352 .adjustForHiddenColumns(av.endRes))
354 if (!ap.scrollRight(true))
361 setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
362 seqCanvas.cursorX, seqCanvas.cursorY);
367 void setSelectionAreaAtCursor(boolean topLeft)
369 SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
371 if (av.getSelectionGroup() != null)
373 SequenceGroup sg = av.getSelectionGroup();
374 // Find the top and bottom of this group
375 int min = av.getAlignment().getHeight(), max = 0;
376 for (int i = 0; i < sg.getSize(); i++)
378 int index = av.getAlignment().findIndex(sg.getSequenceAt(i));
393 sg.setStartRes(seqCanvas.cursorX);
394 if (sg.getEndRes() < seqCanvas.cursorX)
396 sg.setEndRes(seqCanvas.cursorX);
399 min = seqCanvas.cursorY;
403 sg.setEndRes(seqCanvas.cursorX);
404 if (sg.getStartRes() > seqCanvas.cursorX)
406 sg.setStartRes(seqCanvas.cursorX);
409 max = seqCanvas.cursorY + 1;
414 // Only the user can do this
415 av.setSelectionGroup(null);
419 // Now add any sequences between min and max
420 sg.getSequences(null).clear();
421 for (int i = min; i < max; i++)
423 sg.addSequence(av.getAlignment().getSequenceAt(i), false);
428 if (av.getSelectionGroup() == null)
430 SequenceGroup sg = new SequenceGroup();
431 sg.setStartRes(seqCanvas.cursorX);
432 sg.setEndRes(seqCanvas.cursorX);
433 sg.addSequence(sequence, false);
434 av.setSelectionGroup(sg);
437 ap.paintAlignment(false);
441 void insertGapAtCursor(boolean group)
443 groupEditing = group;
444 startseq = seqCanvas.cursorY;
445 lastres = seqCanvas.cursorX;
446 editSequence(true, false, seqCanvas.cursorX + getKeyboardNo1());
450 void deleteGapAtCursor(boolean group)
452 groupEditing = group;
453 startseq = seqCanvas.cursorY;
454 lastres = seqCanvas.cursorX + getKeyboardNo1();
455 editSequence(false, false, seqCanvas.cursorX);
459 void insertNucAtCursor(boolean group, String nuc)
461 groupEditing = group;
462 startseq = seqCanvas.cursorY;
463 lastres = seqCanvas.cursorX;
464 editSequence(false, true, seqCanvas.cursorX + getKeyboardNo1());
468 void numberPressed(char value)
470 if (keyboardNo1 == null)
472 keyboardNo1 = new StringBuffer();
475 if (keyboardNo2 != null)
477 keyboardNo2.append(value);
481 keyboardNo1.append(value);
489 if (keyboardNo1 != null)
491 int value = Integer.parseInt(keyboardNo1.toString());
495 } catch (Exception x)
506 if (keyboardNo2 != null)
508 int value = Integer.parseInt(keyboardNo2.toString());
512 } catch (Exception x)
526 public void mouseReleased(MouseEvent evt)
528 mouseDragging = false;
529 mouseWheelPressed = false;
533 doMouseReleasedDefineMode(evt);
547 public void mousePressed(MouseEvent evt)
549 lastMousePress = evt.getPoint();
551 if (javax.swing.SwingUtilities.isMiddleMouseButton(evt))
553 mouseWheelPressed = true;
557 if (evt.isShiftDown() || evt.isAltDown() || evt.isControlDown())
559 if (evt.isAltDown() || evt.isControlDown())
567 doMousePressedDefineMode(evt);
571 int seq = findSeq(evt);
572 int res = findRes(evt);
574 if (seq < 0 || res < 0)
579 if ((seq < av.getAlignment().getHeight())
580 && (res < av.getAlignment().getSequenceAt(seq).getLength()))
597 public void mouseOverSequence(SequenceI sequence, int index, int pos)
599 String tmp = sequence.hashCode() + " " + index + " " + pos;
601 if (lastMessage == null || !lastMessage.equals(tmp))
603 // System.err.println("mouseOver Sequence: "+tmp);
604 ssm.mouseOverSequence(sequence, index, pos, av);
610 public void highlightSequence(SearchResults results)
612 if (av.followHighlight)
614 if (ap.scrollToPosition(results, false))
616 seqCanvas.revalidate();
619 seqCanvas.highlightSearchResults(results);
623 public void updateColours(SequenceI seq, int index)
625 System.out.println("update the seqPanel colours");
636 public void mouseMoved(MouseEvent evt)
640 // This is because MacOSX creates a mouseMoved
641 // If control is down, other platforms will not.
645 int res = findRes(evt);
646 int seq = findSeq(evt);
648 if (res < 0 || seq < 0 || seq >= av.getAlignment().getHeight())
653 SequenceI sequence = av.getAlignment().getSequenceAt(seq);
655 if (res >= sequence.getLength())
660 pos = setStatusMessage(sequence, res, seq);
661 if (ssm != null && pos > -1)
662 mouseOverSequence(sequence, res, pos);
664 tooltipText.setLength(6); // Cuts the buffer back to <html>
666 SequenceGroup[] groups = av.getAlignment().findAllGroups(sequence);
669 for (int g = 0; g < groups.length; g++)
671 if (groups[g].getStartRes() <= res && groups[g].getEndRes() >= res)
673 if (tooltipText.length() > 6)
675 tooltipText.append("<br>");
678 if (!groups[g].getName().startsWith("JTreeGroup")
679 && !groups[g].getName().startsWith("JGroup"))
681 tooltipText.append(groups[g].getName());
684 if (groups[g].getDescription() != null)
686 tooltipText.append(": " + groups[g].getDescription());
692 // use aa to see if the mouse pointer is on a
693 if (av.isShowSequenceFeatures())
696 List<SequenceFeature> features = ap.getFeatureRenderer().findFeaturesAtRes(
697 sequence.getDatasetSequence(),
698 rpos = sequence.findPosition(res));
699 seqARep.appendFeatures(tooltipText, rpos, features,
700 this.ap.seqPanel.seqCanvas.fr.getMinMax());
702 if (tooltipText.length() == 6) // <html></html>
704 setToolTipText(null);
709 tooltipText.append("</html>");
710 if (lastTooltip == null
711 || !lastTooltip.equals(tooltipText.toString()))
713 setToolTipText(tooltipText.toString());
714 lastTooltip = tooltipText.toString();
721 private Point lastp = null;
726 * @see javax.swing.JComponent#getToolTipLocation(java.awt.event.MouseEvent)
728 public Point getToolTipLocation(MouseEvent event)
730 int x = event.getX(), w = getWidth();
731 int wdth = (w - x < 200) ? -(w / 2) : 5; // switch sides when tooltip is too
734 if (!event.isShiftDown() || p == null)
736 p = (tooltipText != null && tooltipText.length() > 6) ? new Point(
737 event.getX() + wdth, event.getY() - 20) : null;
740 * TODO: try to modify position region is not obcured by tooltip
748 * Set status message in alignment panel
751 * aligned sequence object
755 * index of sequence in alignment
756 * @return position of res in sequence
758 int setStatusMessage(SequenceI sequence, int res, int seq)
761 StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: "
762 + sequence.getName());
765 if (av.getAlignment().isNucleotide())
767 obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)
771 text.append(" Nucleotide: ");
776 obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
779 text.append(" Residue: ");
785 pos = sequence.findPosition(res);
788 text.append(obj + " (" + pos + ")");
791 ap.alignFrame.statusBar.setText(text.toString());
802 public void mouseDragged(MouseEvent evt)
804 if (mouseWheelPressed)
806 int oldWidth = av.charWidth;
808 // Which is bigger, left-right or up-down?
809 if (Math.abs(evt.getY() - lastMousePress.getY()) > Math.abs(evt
810 .getX() - lastMousePress.getX()))
812 int fontSize = av.font.getSize();
814 if (evt.getY() < lastMousePress.getY())
818 else if (evt.getY() > lastMousePress.getY())
828 av.setFont(new Font(av.font.getName(), av.font.getStyle(), fontSize));
829 av.charWidth = oldWidth;
834 if (evt.getX() < lastMousePress.getX() && av.charWidth > 1)
838 else if (evt.getX() > lastMousePress.getX())
843 ap.paintAlignment(false);
846 FontMetrics fm = getFontMetrics(av.getFont());
847 av.validCharWidth = fm.charWidth('M') <= av.charWidth;
849 lastMousePress = evt.getPoint();
856 doMouseDraggedDefineMode(evt);
860 int res = findRes(evt);
867 if ((lastres == -1) || (lastres == res))
872 if ((res < av.getAlignment().getWidth()) && (res < lastres))
874 // dragLeft, delete gap
875 editSequence(false, false, res);
879 editSequence(true, false, res);
882 mouseDragging = true;
883 if (scrollThread != null)
885 scrollThread.setEvent(evt);
889 // TODO: Make it more clever than many booleans
890 synchronized void editSequence(boolean insertGap, boolean editSeq,
895 boolean fixedColumns = false;
896 SequenceGroup sg = av.getSelectionGroup();
898 SequenceI seq = av.getAlignment().getSequenceAt(startseq);
900 // No group, but the sequence may represent a group
901 if (!groupEditing && av.hasHiddenRows())
903 if (av.isHiddenRepSequence(seq))
905 sg = av.getRepresentedSequences(seq);
910 StringBuffer message = new StringBuffer();
913 message.append("Edit group:");
914 if (editCommand == null)
916 editCommand = new EditCommand(MessageManager.getString("action.edit_group"));
921 message.append("Edit sequence: " + seq.getName());
922 String label = seq.getName();
923 if (label.length() > 10)
925 label = label.substring(0, 10);
927 if (editCommand == null)
929 editCommand = new EditCommand(MessageManager.formatMessage("label.edit_params", new String[]{label}));
935 message.append(" insert ");
939 message.append(" delete ");
942 message.append(Math.abs(startres - lastres) + " gaps.");
943 ap.alignFrame.statusBar.setText(message.toString());
945 // Are we editing within a selection group?
947 || (sg != null && sg.getSequences(av.getHiddenRepSequences())
952 // sg might be null as the user may only see 1 sequence,
953 // but the sequence represents a group
956 if (!av.isHiddenRepSequence(seq))
961 sg = av.getRepresentedSequences(seq);
964 fixedLeft = sg.getStartRes();
965 fixedRight = sg.getEndRes();
967 if ((startres < fixedLeft && lastres >= fixedLeft)
968 || (startres >= fixedLeft && lastres < fixedLeft)
969 || (startres > fixedRight && lastres <= fixedRight)
970 || (startres <= fixedRight && lastres > fixedRight))
976 if (fixedLeft > startres)
978 fixedRight = fixedLeft - 1;
981 else if (fixedRight < startres)
983 fixedLeft = fixedRight;
988 if (av.hasHiddenColumns())
991 int y1 = av.getColumnSelection().getHiddenBoundaryLeft(startres);
992 int y2 = av.getColumnSelection().getHiddenBoundaryRight(startres);
994 if ((insertGap && startres > y1 && lastres < y1)
995 || (!insertGap && startres < y2 && lastres > y2))
1001 // System.out.print(y1+" "+y2+" "+fixedLeft+" "+fixedRight+"~~");
1002 // Selection spans a hidden region
1003 if (fixedLeft < y1 && (fixedRight > y2 || fixedRight == -1))
1011 fixedRight = y2 - 1;
1018 List<SequenceI> vseqs = sg.getSequences(av.getHiddenRepSequences());
1019 int g, groupSize = vseqs.size();
1020 SequenceI[] groupSeqs = new SequenceI[groupSize];
1021 for (g = 0; g < groupSeqs.length; g++)
1023 groupSeqs[g] = vseqs.get(g);
1029 // If the user has selected the whole sequence, and is dragging to
1030 // the right, we can still extend the alignment and selectionGroup
1031 if (sg.getStartRes() == 0 && sg.getEndRes() == fixedRight
1032 && sg.getEndRes() == av.getAlignment().getWidth() - 1)
1034 sg.setEndRes(av.getAlignment().getWidth() + startres - lastres);
1035 fixedRight = sg.getEndRes();
1038 // Is it valid with fixed columns??
1039 // Find the next gap before the end
1040 // of the visible region boundary
1041 boolean blank = false;
1042 for (fixedRight = fixedRight; fixedRight > lastres; fixedRight--)
1046 for (g = 0; g < groupSize; g++)
1048 for (int j = 0; j < startres - lastres; j++)
1050 if (!jalview.util.Comparison.isGap(groupSeqs[g]
1051 .getCharAt(fixedRight - j)))
1066 if (sg.getSize() == av.getAlignment().getHeight())
1068 if ((av.hasHiddenColumns() && startres < av
1069 .getColumnSelection().getHiddenBoundaryRight(startres)))
1075 int alWidth = av.getAlignment().getWidth();
1076 if (av.hasHiddenRows())
1078 int hwidth = av.getAlignment().getHiddenSequences()
1080 if (hwidth > alWidth)
1085 // We can still insert gaps if the selectionGroup
1086 // contains all the sequences
1087 sg.setEndRes(sg.getEndRes() + startres - lastres);
1088 fixedRight = alWidth + startres - lastres;
1099 else if (!insertGap)
1101 // / Are we able to delete?
1102 // ie are all columns blank?
1104 for (g = 0; g < groupSize; g++)
1106 for (int j = startres; j < lastres; j++)
1108 if (groupSeqs[g].getLength() <= j)
1113 if (!jalview.util.Comparison.isGap(groupSeqs[g].getCharAt(j)))
1115 // Not a gap, block edit not valid
1125 // dragging to the right
1126 if (fixedColumns && fixedRight != -1)
1128 for (int j = lastres; j < startres; j++)
1130 insertChar(j, groupSeqs, fixedRight);
1135 editCommand.appendEdit(EditCommand.INSERT_GAP, groupSeqs,
1136 startres, startres - lastres, av.getAlignment(), true);
1141 // dragging to the left
1142 if (fixedColumns && fixedRight != -1)
1144 for (int j = lastres; j > startres; j--)
1146 deleteChar(startres, groupSeqs, fixedRight);
1151 editCommand.appendEdit(EditCommand.DELETE_GAP, groupSeqs,
1152 startres, lastres - startres, av.getAlignment(), true);
1158 // ///Editing a single sequence///////////
1162 // dragging to the right
1163 if (fixedColumns && fixedRight != -1)
1165 for (int j = lastres; j < startres; j++)
1167 insertChar(j, new SequenceI[]
1168 { seq }, fixedRight);
1173 editCommand.appendEdit(EditCommand.INSERT_GAP, new SequenceI[]
1174 { seq }, lastres, startres - lastres, av.getAlignment(), true);
1181 // dragging to the left
1182 if (fixedColumns && fixedRight != -1)
1184 for (int j = lastres; j > startres; j--)
1186 if (!jalview.util.Comparison.isGap(seq.getCharAt(startres)))
1191 deleteChar(startres, new SequenceI[]
1192 { seq }, fixedRight);
1197 // could be a keyboard edit trying to delete none gaps
1199 for (int m = startres; m < lastres; m++)
1201 if (!jalview.util.Comparison.isGap(seq.getCharAt(m)))
1210 editCommand.appendEdit(EditCommand.DELETE_GAP,
1212 { seq }, startres, max, av.getAlignment(), true);
1217 {// insertGap==false AND editSeq==TRUE;
1218 if (fixedColumns && fixedRight != -1)
1220 for (int j = lastres; j < startres; j++)
1222 insertChar(j, new SequenceI[]
1223 { seq }, fixedRight);
1228 editCommand.appendEdit(EditCommand.INSERT_NUC, new SequenceI[]
1229 { seq }, lastres, startres - lastres, av.getAlignment(), true);
1236 seqCanvas.repaint();
1239 void insertChar(int j, SequenceI[] seq, int fixedColumn)
1241 int blankColumn = fixedColumn;
1242 for (int s = 0; s < seq.length; s++)
1244 // Find the next gap before the end of the visible region boundary
1245 // If lastCol > j, theres a boundary after the gap insertion
1247 for (blankColumn = fixedColumn; blankColumn > j; blankColumn--)
1249 if (jalview.util.Comparison.isGap(seq[s].getCharAt(blankColumn)))
1251 // Theres a space, so break and insert the gap
1256 if (blankColumn <= j)
1258 blankColumn = fixedColumn;
1264 editCommand.appendEdit(EditCommand.DELETE_GAP, seq, blankColumn, 1,
1265 av.getAlignment(), true);
1267 editCommand.appendEdit(EditCommand.INSERT_GAP, seq, j, 1,
1268 av.getAlignment(), true);
1272 void deleteChar(int j, SequenceI[] seq, int fixedColumn)
1275 editCommand.appendEdit(EditCommand.DELETE_GAP, seq, j, 1,
1276 av.getAlignment(), true);
1278 editCommand.appendEdit(EditCommand.INSERT_GAP, seq, fixedColumn, 1,
1279 av.getAlignment(), true);
1289 public void mouseEntered(MouseEvent e)
1296 if (scrollThread != null)
1298 scrollThread.running = false;
1299 scrollThread = null;
1310 public void mouseExited(MouseEvent e)
1312 if (av.getWrapAlignment())
1319 scrollThread = new ScrollThread();
1324 public void mouseClicked(MouseEvent evt)
1326 SequenceGroup sg = null;
1327 SequenceI sequence = av.getAlignment().getSequenceAt(findSeq(evt));
1328 if (evt.getClickCount() > 1)
1330 sg = av.getSelectionGroup();
1331 if (sg != null && sg.getSize() == 1
1332 && sg.getEndRes() - sg.getStartRes() < 2)
1334 av.setSelectionGroup(null);
1337 List<SequenceFeature> features = seqCanvas.getFeatureRenderer().findFeaturesAtRes(
1338 sequence.getDatasetSequence(),
1339 sequence.findPosition(findRes(evt)));
1341 if (features != null && features.size()> 0)
1343 SearchResults highlight = new SearchResults();
1344 highlight.addResult(sequence, features.get(0).getBegin(),
1345 features.get(0).getEnd());
1346 seqCanvas.highlightSearchResults(highlight);
1348 if (features != null && features.size()> 0)
1350 seqCanvas.getFeatureRenderer().amendFeatures(new SequenceI[]
1351 { sequence }, features.toArray(new SequenceFeature[features.size()]), false, ap);
1353 seqCanvas.highlightSearchResults(null);
1359 public void mouseWheelMoved(MouseWheelEvent e)
1362 if (e.getWheelRotation() > 0)
1364 if (e.isShiftDown())
1366 ap.scrollRight(true);
1376 if (e.isShiftDown())
1378 ap.scrollRight(false);
1385 // TODO Update tooltip for new position.
1394 public void doMousePressedDefineMode(MouseEvent evt)
1396 int res = findRes(evt);
1397 int seq = findSeq(evt);
1400 startWrapBlock = wrappedBlock;
1402 if (av.wrapAlignment && seq > av.getAlignment().getHeight())
1404 JOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
1405 .getString("label.cannot_edit_annotations_in_wrapped_view"),
1406 MessageManager.getString("label.wrapped_view_no_edit"),
1407 JOptionPane.WARNING_MESSAGE);
1411 if (seq < 0 || res < 0)
1416 SequenceI sequence = av.getAlignment().getSequenceAt(seq);
1418 if ((sequence == null) || (res > sequence.getLength()))
1423 stretchGroup = av.getSelectionGroup();
1425 if (stretchGroup == null)
1427 stretchGroup = av.getAlignment().findGroup(sequence);
1429 if ((stretchGroup != null) && (res > stretchGroup.getStartRes())
1430 && (res < stretchGroup.getEndRes()))
1432 av.setSelectionGroup(stretchGroup);
1436 stretchGroup = null;
1439 else if (!stretchGroup.getSequences(null).contains(sequence)
1440 || (stretchGroup.getStartRes() > res)
1441 || (stretchGroup.getEndRes() < res))
1443 stretchGroup = null;
1445 SequenceGroup[] allGroups = av.getAlignment().findAllGroups(sequence);
1447 if (allGroups != null)
1449 for (int i = 0; i < allGroups.length; i++)
1451 if ((allGroups[i].getStartRes() <= res)
1452 && (allGroups[i].getEndRes() >= res))
1454 stretchGroup = allGroups[i];
1460 av.setSelectionGroup(stretchGroup);
1464 if (javax.swing.SwingUtilities.isRightMouseButton(evt))
1466 List<SequenceFeature> allFeatures = ap.getFeatureRenderer().findFeaturesAtRes(
1467 sequence.getDatasetSequence(), sequence.findPosition(res));
1468 Vector links = new Vector();
1469 for (SequenceFeature sf:allFeatures)
1471 if (sf.links != null)
1473 for (int j = 0; j < sf.links.size(); j++)
1475 links.addElement(sf.links.elementAt(j));
1480 jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu(ap, null, links);
1481 pop.show(this, evt.getX(), evt.getY());
1487 seqCanvas.cursorX = findRes(evt);
1488 seqCanvas.cursorY = findSeq(evt);
1489 seqCanvas.repaint();
1493 if (stretchGroup == null)
1495 // Only if left mouse button do we want to change group sizes
1497 // define a new group here
1498 SequenceGroup sg = new SequenceGroup();
1499 sg.setStartRes(res);
1501 sg.addSequence(sequence, false);
1502 av.setSelectionGroup(sg);
1506 if (av.getConservationSelected())
1508 SliderPanel.setConservationSlider(ap, av.getGlobalColourScheme(),
1512 if (av.getAbovePIDThreshold())
1514 SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(),
1517 if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))
1519 // Edit end res position of selected group
1520 changeEndRes = true;
1522 else if ((stretchGroup != null)
1523 && (stretchGroup.getStartRes() == res))
1525 // Edit end res position of selected group
1526 changeStartRes = true;
1528 stretchGroup.getWidth();
1531 seqCanvas.repaint();
1540 public void doMouseReleasedDefineMode(MouseEvent evt)
1542 if (stretchGroup == null)
1547 stretchGroup.recalcConservation(); // always do this - annotation has own
1549 if (stretchGroup.cs != null)
1551 stretchGroup.cs.alignmentChanged(stretchGroup,
1552 av.getHiddenRepSequences());
1554 if (stretchGroup.cs.conservationApplied())
1556 SliderPanel.setConservationSlider(ap, stretchGroup.cs,
1557 stretchGroup.getName());
1561 SliderPanel.setPIDSliderSource(ap, stretchGroup.cs,
1562 stretchGroup.getName());
1565 PaintRefresher.Refresh(this, av.getSequenceSetId());
1566 ap.paintAlignment(true);
1568 changeEndRes = false;
1569 changeStartRes = false;
1570 stretchGroup = null;
1580 public void doMouseDraggedDefineMode(MouseEvent evt)
1582 int res = findRes(evt);
1583 int y = findSeq(evt);
1585 if (wrappedBlock != startWrapBlock)
1590 if (stretchGroup == null)
1595 if (res >= av.getAlignment().getWidth())
1597 res = av.getAlignment().getWidth() - 1;
1600 if (stretchGroup.getEndRes() == res)
1602 // Edit end res position of selected group
1603 changeEndRes = true;
1605 else if (stretchGroup.getStartRes() == res)
1607 // Edit start res position of selected group
1608 changeStartRes = true;
1611 if (res < av.getStartRes())
1613 res = av.getStartRes();
1618 if (res > (stretchGroup.getStartRes() - 1))
1620 stretchGroup.setEndRes(res);
1623 else if (changeStartRes)
1625 if (res < (stretchGroup.getEndRes() + 1))
1627 stretchGroup.setStartRes(res);
1631 int dragDirection = 0;
1637 else if (y < oldSeq)
1642 while ((y != oldSeq) && (oldSeq > -1)
1643 && (y < av.getAlignment().getHeight()))
1645 // This routine ensures we don't skip any sequences, as the
1646 // selection is quite slow.
1647 Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);
1649 oldSeq += dragDirection;
1656 Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);
1658 if (stretchGroup.getSequences(null).contains(nextSeq))
1660 stretchGroup.deleteSequence(seq, false);
1666 stretchGroup.addSequence(seq, false);
1669 stretchGroup.addSequence(nextSeq, false);
1678 mouseDragging = true;
1680 if (scrollThread != null)
1682 scrollThread.setEvent(evt);
1685 seqCanvas.repaint();
1688 void scrollCanvas(MouseEvent evt)
1692 if (scrollThread != null)
1694 scrollThread.running = false;
1695 scrollThread = null;
1697 mouseDragging = false;
1701 if (scrollThread == null)
1703 scrollThread = new ScrollThread();
1706 mouseDragging = true;
1707 scrollThread.setEvent(evt);
1712 // this class allows scrolling off the bottom of the visible alignment
1713 class ScrollThread extends Thread
1717 boolean running = false;
1719 public ScrollThread()
1724 public void setEvent(MouseEvent e)
1729 public void stopScrolling()
1743 if (mouseDragging && (evt.getY() < 0) && (av.getStartSeq() > 0))
1745 running = ap.scrollUp(true);
1748 if (mouseDragging && (evt.getY() >= getHeight())
1749 && (av.getAlignment().getHeight() > av.getEndSeq()))
1751 running = ap.scrollUp(false);
1754 if (mouseDragging && (evt.getX() < 0))
1756 running = ap.scrollRight(false);
1758 else if (mouseDragging && (evt.getX() >= getWidth()))
1760 running = ap.scrollRight(true);
1767 } catch (Exception ex)
1775 * modify current selection according to a received message.
1778 public void selection(SequenceGroup seqsel, ColumnSelection colsel,
1779 SelectionSource source)
1781 // TODO: fix this hack - source of messages is align viewport, but SeqPanel
1782 // handles selection messages...
1783 // TODO: extend config options to allow user to control if selections may be
1784 // shared between viewports.
1786 || !av.followSelection
1787 || (av.isSelectionGroupChanged(false) || av
1788 .isColSelChanged(false))
1789 || (source instanceof AlignViewport && ((AlignViewport) source)
1790 .getSequenceSetId().equals(av.getSequenceSetId())))
1794 // do we want to thread this ? (contention with seqsel and colsel locks, I
1796 // rules are: colsel is copied if there is a real intersection between
1797 // sequence selection
1798 boolean repaint = false, copycolsel = true;
1799 // if (!av.isSelectionGroupChanged(false))
1801 SequenceGroup sgroup = null;
1802 if (seqsel != null && seqsel.getSize() > 0)
1804 if (av.getAlignment() == null)
1806 jalview.bin.Cache.log.warn("alignviewport av SeqSetId="
1807 + av.getSequenceSetId() + " ViewId=" + av.getViewId()
1808 + " 's alignment is NULL! returning immediatly.");
1811 sgroup = seqsel.intersect(av.getAlignment(),
1812 (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null);
1813 if ((sgroup == null || sgroup.getSize() == 0)
1814 || (colsel == null || colsel.size() == 0))
1816 // don't copy columns if the region didn't intersect.
1820 if (sgroup != null && sgroup.getSize() > 0)
1822 av.setSelectionGroup(sgroup);
1826 av.setSelectionGroup(null);
1828 av.isSelectionGroupChanged(true);
1833 // the current selection is unset or from a previous message
1834 // so import the new colsel.
1835 if (colsel == null || colsel.size() == 0)
1837 if (av.getColumnSelection() != null)
1839 av.getColumnSelection().clear();
1845 // TODO: shift colSel according to the intersecting sequences
1846 if (av.getColumnSelection() == null)
1848 av.setColumnSelection(new ColumnSelection(colsel));
1852 av.getColumnSelection().setElementsFrom(colsel);
1855 av.isColSelChanged(true);
1859 && av.hasHiddenColumns()
1860 && (av.getColumnSelection() == null || av.getColumnSelection()
1861 .getHiddenColumns() == null))
1863 System.err.println("Bad things");
1867 // probably finessing with multiple redraws here
1868 PaintRefresher.Refresh(this, av.getSequenceSetId());
1869 // ap.paintAlignment(false);