X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fjalview%2Fgui%2FSeqPanel.java;h=2df55ad53080f9a8c9eeb8a9c4f2a57636d9e3c1;hb=87256ba36fb105a1115067ffe2563412e9281d2d;hp=a53edb9203a4f55080fb11989713db3c24314fc2;hpb=f484ffbf14ae725e55c80396be1889708988fd8b;p=jalview.git diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index a53edb9..2df55ad 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -49,12 +49,15 @@ import jalview.util.MappingUtils; import jalview.util.MessageManager; import jalview.util.Platform; import jalview.viewmodel.AlignmentViewport; +import jalview.viewmodel.ViewportRanges; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; @@ -63,8 +66,11 @@ import java.awt.event.MouseWheelListener; import java.util.Collections; import java.util.List; +import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JToolTip; import javax.swing.SwingUtilities; +import javax.swing.Timer; import javax.swing.ToolTipManager; /** @@ -164,9 +170,9 @@ public class SeqPanel extends JPanel */ private MousePos lastMousePosition; - protected int lastres; + protected int editLastRes; - protected int startseq; + protected int editStartSeq; protected AlignViewport av; @@ -230,6 +236,8 @@ public class SeqPanel extends JPanel ToolTipManager.sharedInstance().registerComponent(this); ToolTipManager.sharedInstance().setInitialDelay(0); ToolTipManager.sharedInstance().setDismissDelay(10000); + + this.av = viewport; setBackground(Color.white); @@ -313,8 +321,10 @@ public class SeqPanel extends JPanel } else { - seqIndex = Math.min((y / charHeight) + av.getRanges().getStartSeq(), + ViewportRanges ranges = av.getRanges(); + seqIndex = Math.min((y / charHeight) + ranges.getStartSeq(), alignmentHeight - 1); + seqIndex = Math.min(seqIndex, ranges.getEndSeq()); } return new MousePos(col, seqIndex, annIndex); @@ -411,8 +421,8 @@ public class SeqPanel extends JPanel /* * Tidy up come what may... */ - startseq = -1; - lastres = -1; + editStartSeq = -1; + editLastRes = -1; editingSeqs = false; groupEditing = false; keyboardNo1 = null; @@ -640,8 +650,8 @@ public class SeqPanel extends JPanel void insertGapAtCursor(boolean group) { groupEditing = group; - startseq = seqCanvas.cursorY; - lastres = seqCanvas.cursorX; + editStartSeq = seqCanvas.cursorY; + editLastRes = seqCanvas.cursorX; editSequence(true, false, seqCanvas.cursorX + getKeyboardNo1()); endEditing(); } @@ -649,8 +659,8 @@ public class SeqPanel extends JPanel void deleteGapAtCursor(boolean group) { groupEditing = group; - startseq = seqCanvas.cursorY; - lastres = seqCanvas.cursorX + getKeyboardNo1(); + editStartSeq = seqCanvas.cursorY; + editLastRes = seqCanvas.cursorX + getKeyboardNo1(); editSequence(false, false, seqCanvas.cursorX); endEditing(); } @@ -659,8 +669,8 @@ public class SeqPanel extends JPanel { // TODO not called - delete? groupEditing = group; - startseq = seqCanvas.cursorY; - lastres = seqCanvas.cursorX; + editStartSeq = seqCanvas.cursorY; + editLastRes = seqCanvas.cursorX; editSequence(false, true, seqCanvas.cursorX + getKeyboardNo1()); endEditing(); } @@ -742,13 +752,14 @@ public class SeqPanel extends JPanel return; } - if (!editingSeqs) + if (editingSeqs) + { + endEditing(); + } + else { doMouseReleasedDefineMode(evt, didDrag); - return; } - - endEditing(); } /** @@ -794,13 +805,13 @@ public class SeqPanel extends JPanel if ((seq < av.getAlignment().getHeight()) && (res < av.getAlignment().getSequenceAt(seq).getLength())) { - startseq = seq; - lastres = res; + editStartSeq = seq; + editLastRes = res; } else { - startseq = -1; - lastres = -1; + editStartSeq = -1; + editLastRes = -1; } return; @@ -808,6 +819,8 @@ public class SeqPanel extends JPanel String lastMessage; + private String formattedTooltipText; + @Override public void mouseOverSequence(SequenceI sequence, int index, int pos) { @@ -845,7 +858,7 @@ public class SeqPanel extends JPanel // over residue to change abruptly, causing highlighted residue in panel 2 // to change, causing a scroll in panel 1 etc) ap.setToScrollComplementPanel(false); - wasScrolled = ap.scrollToPosition(results, false); + wasScrolled = ap.scrollToPosition(results); if (wasScrolled) { seqCanvas.revalidate(); @@ -853,8 +866,8 @@ public class SeqPanel extends JPanel ap.setToScrollComplementPanel(true); } - boolean noFastPaint = wasScrolled && av.getWrapAlignment(); - if (seqCanvas.highlightSearchResults(results, noFastPaint)) + boolean fastPaint = !(wasScrolled && av.getWrapAlignment()); + if (seqCanvas.highlightSearchResults(results, fastPaint)) { setStatusMessage(results); } @@ -935,7 +948,7 @@ public class SeqPanel extends JPanel mouseOverSequence(sequence, column, pos); } - tooltipText.setLength(6); // Cuts the buffer back to + tooltipText.setLength(6); // "" SequenceGroup[] groups = av.getAlignment().findAllGroups(sequence); if (groups != null) @@ -986,9 +999,9 @@ public class SeqPanel extends JPanel String textString = tooltipText.toString(); if (lastTooltip == null || !lastTooltip.equals(textString)) { - String formattedTooltipText = JvSwingUtils.wrapTooltip(true, + formattedTooltipText = JvSwingUtils.wrapTooltip(true, textString); - setToolTipText(formattedTooltipText); + setToolTipText(formattedTooltipText); lastTooltip = textString; } } @@ -1025,6 +1038,8 @@ public class SeqPanel extends JPanel private Point lastp = null; + private JToolTip tempTip = new JLabel().createToolTip(); + /* * (non-Javadoc) * @@ -1033,26 +1048,31 @@ public class SeqPanel extends JPanel @Override public Point getToolTipLocation(MouseEvent event) { + // BH 2018 + if (tooltipText == null || tooltipText.length() <= 6) { - lastp = null; return null; } - int x = event.getX(); - int w = getWidth(); - // switch sides when tooltip is too close to edge - int wdth = (w - x < 200) ? -(w / 2) : 5; - Point p = lastp; - if (!event.isShiftDown() || p == null) + if (lastp != null && event.isShiftDown()) { - p = new Point(event.getX() + wdth, event.getY() - 20); - lastp = p; + return lastp; } - /* - * TODO: try to set position so region is not obscured by tooltip - */ - return p; + + Point p = lastp; + int x = event.getX(); + int y = event.getY(); + int w = getWidth(); + + tempTip.setTipText(formattedTooltipText); + int tipWidth = (int) tempTip.getPreferredSize().getWidth(); + + // was x += (w - x < 200) ? -(w / 2) : 5; + x = (x + tipWidth < w ? x + 10 : w - tipWidth); + p = new Point(x, y + 20); // BH 2018 was - 20? + + return lastp = p; } String lastTooltip; @@ -1309,12 +1329,12 @@ public class SeqPanel extends JPanel res = 0; } - if ((lastres == -1) || (lastres == res)) + if ((editLastRes == -1) || (editLastRes == res)) { return; } - if ((res < av.getAlignment().getWidth()) && (res < lastres)) + if ((res < av.getAlignment().getWidth()) && (res < editLastRes)) { // dragLeft, delete gap editSequence(false, false, res); @@ -1325,22 +1345,46 @@ public class SeqPanel extends JPanel } mouseDragging = true; - if ((scrollThread != null) && (scrollThread.isRunning())) + if (scrollThread != null) { - scrollThread.setEvent(evt); + scrollThread.setMousePosition(evt.getPoint()); } } - // TODO: Make it more clever than many booleans + /** + * Edits the sequence to insert or delete one or more gaps, in response to a + * mouse drag or cursor mode command. The number of inserts/deletes may be + * specified with the cursor command, or else depends on the mouse event + * (normally one column, but potentially more for a fast mouse drag). + *
+ * Delete gaps is limited to the number of gaps left of the cursor position + * (mouse drag), or at or right of the cursor position (cursor mode). + *
+ * In group editing mode (Ctrl or Cmd down), the edit acts on all sequences in + * the current selection group. + *
+ * In locked editing mode (with a selection group present), inserts/deletions
+ * within the selection group are limited to its boundaries (and edits outside
+ * the group stop at its border).
+ *
+ * @param insertGap
+ * true to insert gaps, false to delete gaps
+ * @param editSeq
+ * (unused parameter)
+ * @param startres
+ * the column at which to perform the action; the number of columns
+ * affected depends on this.editLastRes
(cursor column
+ * position)
+ */
synchronized void editSequence(boolean insertGap, boolean editSeq,
- int startres)
+ final int startres)
{
int fixedLeft = -1;
int fixedRight = -1;
boolean fixedColumns = false;
SequenceGroup sg = av.getSelectionGroup();
- SequenceI seq = av.getAlignment().getSequenceAt(startseq);
+ final SequenceI seq = av.getAlignment().getSequenceAt(editStartSeq);
// No group, but the sequence may represent a group
if (!groupEditing && av.hasHiddenRows())
@@ -1352,30 +1396,38 @@ public class SeqPanel extends JPanel
}
}
- StringBuilder message = new StringBuilder(64);
+ StringBuilder message = new StringBuilder(64); // for status bar
+
+ /*
+ * make a name for the edit action, for
+ * status bar message and Undo/Redo menu
+ */
+ String label = null;
if (groupEditing)
{
- message.append("Edit group:");
- if (editCommand == null)
- {
- editCommand = new EditCommand(
- MessageManager.getString("action.edit_group"));
- }
+ message.append("Edit group:");
+ label = MessageManager.getString("action.edit_group");
}
else
{
- message.append("Edit sequence: " + seq.getName());
- String label = seq.getName();
+ message.append("Edit sequence: " + seq.getName());
+ label = seq.getName();
if (label.length() > 10)
{
label = label.substring(0, 10);
}
- if (editCommand == null)
- {
- editCommand = new EditCommand(MessageManager
- .formatMessage("label.edit_params", new String[]
- { label }));
- }
+ label = MessageManager.formatMessage("label.edit_params",
+ new String[]
+ { label });
+ }
+
+ /*
+ * initialise the edit command if there is not
+ * already one being extended
+ */
+ if (editCommand == null)
+ {
+ editCommand = new EditCommand(label);
}
if (insertGap)
@@ -1387,12 +1439,17 @@ public class SeqPanel extends JPanel
message.append(" delete ");
}
- message.append(Math.abs(startres - lastres) + " gaps.");
+ message.append(Math.abs(startres - editLastRes) + " gaps.");
ap.alignFrame.setStatus(message.toString());
- // Are we editing within a selection group?
- if (groupEditing || (sg != null
- && sg.getSequences(av.getHiddenRepSequences()).contains(seq)))
+ /*
+ * is there a selection group containing the sequence being edited?
+ * if so the boundary of the group is the limit of the edit
+ * (but the edit may be inside or outside the selection group)
+ */
+ boolean inSelectionGroup = sg != null
+ && sg.getSequences(av.getHiddenRepSequences()).contains(seq);
+ if (groupEditing || inSelectionGroup)
{
fixedColumns = true;
@@ -1411,10 +1468,10 @@ public class SeqPanel extends JPanel
fixedLeft = sg.getStartRes();
fixedRight = sg.getEndRes();
- if ((startres < fixedLeft && lastres >= fixedLeft)
- || (startres >= fixedLeft && lastres < fixedLeft)
- || (startres > fixedRight && lastres <= fixedRight)
- || (startres <= fixedRight && lastres > fixedRight))
+ if ((startres < fixedLeft && editLastRes >= fixedLeft)
+ || (startres >= fixedLeft && editLastRes < fixedLeft)
+ || (startres > fixedRight && editLastRes <= fixedRight)
+ || (startres <= fixedRight && editLastRes > fixedRight))
{
endEditing();
return;
@@ -1440,8 +1497,8 @@ public class SeqPanel extends JPanel
int y2 = av.getAlignment().getHiddenColumns()
.getNextHiddenBoundary(false, startres);
- if ((insertGap && startres > y1 && lastres < y1)
- || (!insertGap && startres < y2 && lastres > y2))
+ if ((insertGap && startres > y1 && editLastRes < y1)
+ || (!insertGap && startres < y2 && editLastRes > y2))
{
endEditing();
return;
@@ -1462,6 +1519,54 @@ public class SeqPanel extends JPanel
}
}
+ boolean success = doEditSequence(insertGap, editSeq, startres,
+ fixedRight, fixedColumns, sg);
+
+ /*
+ * report what actually happened (might be less than
+ * what was requested), by inspecting the edit commands added
+ */
+ String msg = getEditStatusMessage(editCommand);
+ ap.alignFrame.setStatus(msg == null ? " " : msg);
+ if (!success)
+ {
+ endEditing();
+ }
+
+ editLastRes = startres;
+ seqCanvas.repaint();
+ }
+
+ /**
+ * A helper method that performs the requested editing to insert or delete
+ * gaps (if possible). Answers true if the edit was successful, false if could
+ * only be performed in part or not at all. Failure may occur in 'locked edit'
+ * mode, when an insertion requires a matching gapped position (or column) to
+ * delete, and deletion requires an adjacent gapped position (or column) to
+ * remove.
+ *
+ * @param insertGap
+ * true if inserting gap(s), false if deleting
+ * @param editSeq
+ * (unused parameter, currently always false)
+ * @param startres
+ * the column at which to perform the edit
+ * @param fixedRight
+ * fixed right boundary column of a locked edit (within or to the
+ * left of a selection group)
+ * @param fixedColumns
+ * true if this is a locked edit
+ * @param sg
+ * the sequence group (if group edit is being performed)
+ * @return
+ */
+ protected boolean doEditSequence(final boolean insertGap,
+ final boolean editSeq, final int startres, int fixedRight,
+ final boolean fixedColumns, final SequenceGroup sg)
+ {
+ final SequenceI seq = av.getAlignment().getSequenceAt(editStartSeq);
+ SequenceI[] seqs = new SequenceI[] { seq };
+
if (groupEditing)
{
List
+ *
+ * Answers true if a scroll was performed, false if not - meaning either
+ * that the mouse position is within the panel, or the edge of the alignment
+ * has been reached.
+ */
+ boolean scrollOnce()
+ {
+ /*
+ * quit after mouseUp ensures interrupt in JalviewJS
+ */
+ if (!mouseDragging)
+ {
+ return false;
+ }
+
+ boolean scrolled = false;
+ ViewportRanges ranges = SeqPanel.this.av.getRanges();
+
+ /*
+ * scroll up or down
+ */
+ if (mousePos.y < 0)
+ {
+ // mouse is above this panel - try scroll up
+ scrolled = ranges.scrollUp(true);
+ }
+ else if (mousePos.y >= getHeight())
+ {
+ // mouse is below this panel - try scroll down
+ scrolled = ranges.scrollUp(false);
+ }
+
+ /*
+ * scroll left or right
+ */
+ if (mousePos.x < 0)
+ {
+ scrolled |= ranges.scrollRight(false);
+ }
+ else if (mousePos.x >= getWidth())
+ {
+ scrolled |= ranges.scrollRight(true);
+ }
+ return scrolled;
}
}
@@ -2447,7 +2745,7 @@ public class SeqPanel extends JPanel
HiddenColumns hs = new HiddenColumns();
MappingUtils.mapColumnSelection(colsel, hidden, sourceAv, av, cs, hs);
av.setColumnSelection(cs);
- av.getAlignment().setHiddenColumns(hs);
+ boolean hiddenChanged = av.getAlignment().setHiddenColumns(hs);
// lastly, update any dependent dialogs
if (ap.getCalculationDialog() != null)
@@ -2455,7 +2753,11 @@ public class SeqPanel extends JPanel
ap.getCalculationDialog().validateCalcTypes();
}
- PaintRefresher.Refresh(this, av.getSequenceSetId());
+ /*
+ * repaint alignment, and also Overview or Structure
+ * if hidden column selection has changed
+ */
+ ap.paintAlignment(hiddenChanged, hiddenChanged);
return true;
}