X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FSeqPanel.java;h=1d943f05de7f02ff508207aaf2862fb652da3cf2;hb=aaead18c6acb21da4d63f3ad9f454d9eb410152f;hp=fb6efe5f4d90102192b1ced6235744bdcc249c64;hpb=56e67e558885f901e565620b3710e42909a68d29;p=jalview.git diff --git a/src/jalview/gui/SeqPanel.java b/src/jalview/gui/SeqPanel.java index fb6efe5..1d943f0 100644 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@ -22,6 +22,7 @@ package jalview.gui; import jalview.api.AlignViewportI; import jalview.bin.Cache; +import jalview.bin.Jalview; import jalview.commands.EditCommand; import jalview.commands.EditCommand.Action; import jalview.commands.EditCommand.Edit; @@ -48,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; @@ -62,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; /** @@ -158,6 +165,8 @@ public class SeqPanel extends JPanel ToolTipManager.sharedInstance().registerComponent(this); ToolTipManager.sharedInstance().setInitialDelay(0); ToolTipManager.sharedInstance().setDismissDelay(10000); + + this.av = viewport; setBackground(Color.white); @@ -246,7 +255,7 @@ public class SeqPanel extends JPanel if (av.hasHiddenColumns()) { res = av.getAlignment().getHiddenColumns() - .adjustForHiddenColumns(res); + .visibleToAbsoluteColumn(res); } return res; @@ -359,13 +368,25 @@ public class SeqPanel extends JPanel int original = seqCanvas.cursorX - dx; int maxWidth = av.getAlignment().getWidth(); - // TODO: once JAL-2759 is ready, change this loop to something more - // efficient - while (!hidden.isVisible(seqCanvas.cursorX) - && seqCanvas.cursorX < maxWidth && seqCanvas.cursorX > 0 - && dx != 0) + if (!hidden.isVisible(seqCanvas.cursorX)) { - seqCanvas.cursorX += dx; + int visx = hidden.absoluteToVisibleColumn(seqCanvas.cursorX - dx); + int[] region = hidden.getRegionWithEdgeAtRes(visx); + + if (region != null) // just in case + { + if (dx == 1) + { + // moving right + seqCanvas.cursorX = region[1] + 1; + } + else if (dx == -1) + { + // moving left + seqCanvas.cursorX = region[0] - 1; + } + } + seqCanvas.cursorX = (seqCanvas.cursorX < 0) ? 0 : seqCanvas.cursorX; } if (seqCanvas.cursorX >= maxWidth @@ -420,7 +441,7 @@ public class SeqPanel extends JPanel { // scrollToWrappedVisible expects x-value to have hidden cols subtracted int x = av.getAlignment().getHiddenColumns() - .findColumnPosition(seqCanvas.cursorX); + .absoluteToVisibleColumn(seqCanvas.cursorX); av.getRanges().scrollToWrappedVisible(x); } else @@ -616,13 +637,14 @@ public class SeqPanel extends JPanel return; } - if (!editingSeqs) + if (editingSeqs) + { + endEditing(); + } + else { doMouseReleasedDefineMode(evt, didDrag); - return; } - - endEditing(); } /** @@ -682,6 +704,8 @@ public class SeqPanel extends JPanel String lastMessage; + private String formattedTooltipText; + @Override public void mouseOverSequence(SequenceI sequence, int index, int pos) { @@ -727,8 +751,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); } @@ -851,16 +875,20 @@ 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); + lastTooltip = textString; } } } + private Point lastp = null; + private JToolTip tempTip = new JLabel().createToolTip(); + /* * (non-Javadoc) * @@ -869,19 +897,31 @@ public class SeqPanel extends JPanel @Override public Point getToolTipLocation(MouseEvent event) { - int x = event.getX(), w = getWidth(); - int wdth = (w - x < 200) ? -(w / 2) : 5; // switch sides when tooltip is too - // close to edge + // BH 2018 + + if (tooltipText == null || tooltipText.length() == 6) + return null; + + if (lastp != null && event.isShiftDown()) + return lastp; + Point p = lastp; - if (!event.isShiftDown() || p == null) - { - p = (tooltipText != null && tooltipText.length() > 6) - ? new Point(event.getX() + wdth, event.getY() - 20) - : null; - } + 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? /* * TODO: try to modify position region is not obcured by tooltip + * + * Done? */ + return lastp = p; } @@ -1149,9 +1189,9 @@ public class SeqPanel extends JPanel } mouseDragging = true; - if ((scrollThread != null) && (scrollThread.isRunning())) + if (scrollThread != null) { - scrollThread.setEvent(evt); + scrollThread.setMousePosition(evt.getPoint()); } } @@ -1260,9 +1300,9 @@ public class SeqPanel extends JPanel { fixedColumns = true; int y1 = av.getAlignment().getHiddenColumns() - .getHiddenBoundaryLeft(startres); + .getNextHiddenBoundary(true, startres); int y2 = av.getAlignment().getHiddenColumns() - .getHiddenBoundaryRight(startres); + .getNextHiddenBoundary(false, startres); if ((insertGap && startres > y1 && lastres < y1) || (!insertGap && startres < y2 && lastres > y2)) @@ -1338,7 +1378,8 @@ public class SeqPanel extends JPanel if (sg.getSize() == av.getAlignment().getHeight()) { if ((av.hasHiddenColumns() && startres < av.getAlignment() - .getHiddenColumns().getHiddenBoundaryRight(startres))) + .getHiddenColumns() + .getNextHiddenBoundary(false, startres))) { endEditing(); return; @@ -1562,10 +1603,10 @@ public class SeqPanel extends JPanel } /** - * DOCUMENT ME! + * On reentering the panel, stops any scrolling that was started on dragging + * out of the panel * * @param e - * DOCUMENT ME! */ @Override public void mouseEntered(MouseEvent e) @@ -1574,31 +1615,21 @@ public class SeqPanel extends JPanel { oldSeq = 0; } - - if ((scrollThread != null) && (scrollThread.isRunning())) - { - scrollThread.stopScrolling(); - scrollThread = null; - } + stopScrolling(); } /** - * DOCUMENT ME! + * On leaving the panel, if the mouse is being dragged, starts a thread to + * scroll it until the mouse is released (in unwrapped mode only) * * @param e - * DOCUMENT ME! */ @Override public void mouseExited(MouseEvent e) { - if (av.getWrapAlignment()) - { - return; - } - - if (mouseDragging && scrollThread == null) + if (mouseDragging) { - scrollThread = new ScrollThread(); + startScrolling(e.getPoint()); } } @@ -1638,17 +1669,13 @@ public class SeqPanel extends JPanel SearchResultsI highlight = new SearchResults(); highlight.addResult(sequence, features.get(0).getBegin(), features .get(0).getEnd()); - seqCanvas.highlightSearchResults(highlight, false); + seqCanvas.highlightSearchResults(highlight, true); /* - * open the Amend Features dialog; clear highlighting afterwards, - * whether changes were made or not + * open the Amend Features dialog */ - List seqs = Collections.singletonList(sequence); - seqCanvas.getFeatureRenderer().amendFeatures(seqs, features, false, - ap); - av.setSearchResults(null); // clear highlighting - seqCanvas.repaint(); // draw new/amended features + new FeatureEditor(ap, Collections.singletonList(sequence), features, + false).showDialog(); } } } @@ -1657,7 +1684,8 @@ public class SeqPanel extends JPanel public void mouseWheelMoved(MouseWheelEvent e) { e.consume(); - if (e.getWheelRotation() > 0) + double wheelRotation = e.getPreciseWheelRotation(); + if (wheelRotation > 0) { if (e.isShiftDown()) { @@ -1669,7 +1697,7 @@ public class SeqPanel extends JPanel av.getRanges().scrollUp(false); } } - else + else if (wheelRotation < 0) { if (e.isShiftDown()) { @@ -1905,10 +1933,7 @@ public class SeqPanel extends JPanel return; } - if (res >= av.getAlignment().getWidth()) - { - res = av.getAlignment().getWidth() - 1; - } + res = Math.min(res, av.getAlignment().getWidth()-1); if (stretchGroup.getEndRes() == res) { @@ -1994,92 +2019,153 @@ public class SeqPanel extends JPanel mouseDragging = true; - if ((scrollThread != null) && (scrollThread.isRunning())) + if (scrollThread != null) { - scrollThread.setEvent(evt); + scrollThread.setMousePosition(evt.getPoint()); } } - void scrollCanvas(MouseEvent evt) + /** + * Stops the scroll thread if it is running + */ + void stopScrolling() { - if (evt == null) + if (scrollThread != null) { - if ((scrollThread != null) && (scrollThread.isRunning())) - { - scrollThread.stopScrolling(); - scrollThread = null; - } - mouseDragging = false; + scrollThread.stopScrolling(); + scrollThread = null; } - else + mouseDragging = false; + } + + /** + * Starts a thread to scroll the alignment, towards a given mouse position + * outside the panel bounds, unless the alignment is in wrapped mode + * + * @param mousePos + */ + void startScrolling(Point mousePos) + { + /* + * set this.mouseDragging in case this was called from + * a drag in ScalePanel or AnnotationPanel + */ + mouseDragging = true; + if (!av.getWrapAlignment() && scrollThread == null) { - if (scrollThread == null) + scrollThread = new ScrollThread(); + scrollThread.setMousePosition(mousePos); + if (!Jalview.isJS()) { - scrollThread = new ScrollThread(); + /* + * Java - run in a new thread + */ + scrollThread.start(); + } + else + { + /* + * Javascript - run every 20ms until scrolling stopped + * or reaches the limit of scrollable alignment + */ + // java.util.Timer version: + // Timer t = new Timer("ScrollThreadTimer", true); + // TimerTask task = new TimerTask() + // { + // @Override + // public void run() + // { + // if (!scrollThread.scrollOnce()) + // { + // cancel(); + // } + // } + // }; + // t.schedule(task, 20, 20); + Timer t = new Timer(20, new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + if (scrollThread != null) + { + // if (!scrollOnce() {t.stop();}) gives compiler error :-( + scrollThread.scrollOnce(); + } + } + }); + t.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + if (scrollThread == null) + { + // finished and nulled itself + t.stop(); + } + } + }); + t.start(); } - - mouseDragging = true; - scrollThread.setEvent(evt); } - } - // this class allows scrolling off the bottom of the visible alignment + /** + * Performs scrolling of the visible alignment left, right, up or down, until + * scrolling is stopped by calling stopScrolling, mouse drag is ended, or the + * limit of the alignment is reached + */ class ScrollThread extends Thread { - MouseEvent evt; + private Point mousePos; - private volatile boolean threadRunning = true; + private volatile boolean keepRunning = true; + /** + * Constructor + */ public ScrollThread() { - start(); + setName("SeqPanel$ScrollThread"); } - public void setEvent(MouseEvent e) + /** + * Sets the position of the mouse that determines the direction of the + * scroll to perform. If this is called as the mouse moves, scrolling should + * respond accordingly. For example, if the mouse is dragged right, scroll + * right should start; if the drag continues down, scroll down should also + * happen. + * + * @param p + */ + public void setMousePosition(Point p) { - evt = e; + mousePos = p; } + /** + * Sets a flag that will cause the thread to exit + */ public void stopScrolling() { - threadRunning = false; - } - - public boolean isRunning() - { - return threadRunning; + keepRunning = false; } + /** + * Scrolls the alignment left or right, and/or up or down, depending on the + * last notified mouse position, until the limit of the alignment is + * reached, or a flag is set to stop the scroll + */ @Override public void run() { - while (threadRunning) + while (keepRunning) { - if (evt != null) + if (mousePos != null) { - if (mouseDragging && (evt.getY() < 0) - && (av.getRanges().getStartSeq() > 0)) - { - av.getRanges().scrollUp(true); - } - - if (mouseDragging && (evt.getY() >= getHeight()) && (av - .getAlignment().getHeight() > av.getRanges().getEndSeq())) - { - av.getRanges().scrollUp(false); - } - - if (mouseDragging && (evt.getX() < 0)) - { - av.getRanges().scrollRight(false); - } - else if (mouseDragging && (evt.getX() >= getWidth())) - { - av.getRanges().scrollRight(true); - } + keepRunning = scrollOnce(); } - try { Thread.sleep(20); @@ -2087,6 +2173,60 @@ public class SeqPanel extends JPanel { } } + SeqPanel.this.scrollThread = null; + } + + /** + * Scrolls + * + * 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; } } @@ -2278,4 +2418,13 @@ public class SeqPanel extends JPanel return true; } + + /** + * + * @return null or last search results handled by this panel + */ + public SearchResultsI getLastSearchResults() + { + return lastSearchResults; + } }