JAL-2069 update spike branch with latest
[jalview.git] / src / jalview / gui / SeqPanel.java
index 2b1b0e5..29f68c1 100644 (file)
@@ -59,7 +59,6 @@ import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
 import java.awt.event.MouseWheelEvent;
 import java.awt.event.MouseWheelListener;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -73,15 +72,14 @@ import javax.swing.ToolTipManager;
  * @author $author$
  * @version $Revision: 1.130 $
  */
-public class SeqPanel extends JPanel implements MouseListener,
-        MouseMotionListener, MouseWheelListener, SequenceListener,
-        SelectionListener
-
+public class SeqPanel extends JPanel
+        implements MouseListener, MouseMotionListener, MouseWheelListener,
+        SequenceListener, SelectionListener
 {
-  /** DOCUMENT ME!! */
+  private static final int MAX_TOOLTIP_LENGTH = 300;
+
   public SeqCanvas seqCanvas;
 
-  /** DOCUMENT ME!! */
   public AlignmentPanel ap;
 
   /*
@@ -148,35 +146,33 @@ public class SeqPanel extends JPanel implements MouseListener,
   SearchResultsI lastSearchResults;
 
   /**
-   * Creates a new SeqPanel object.
+   * Creates a new SeqPanel object
    * 
-   * @param avp
-   *          DOCUMENT ME!
-   * @param p
-   *          DOCUMENT ME!
+   * @param viewport
+   * @param alignPanel
    */
-  public SeqPanel(AlignViewport av, AlignmentPanel ap)
+  public SeqPanel(AlignViewport viewport, AlignmentPanel alignPanel)
   {
     linkImageURL = getClass().getResource("/images/link.gif");
     seqARep = new SequenceAnnotationReport(linkImageURL.toString());
     ToolTipManager.sharedInstance().registerComponent(this);
     ToolTipManager.sharedInstance().setInitialDelay(0);
     ToolTipManager.sharedInstance().setDismissDelay(10000);
-    this.av = av;
+    this.av = viewport;
     setBackground(Color.white);
 
-    seqCanvas = new SeqCanvas(ap);
+    seqCanvas = new SeqCanvas(alignPanel);
     setLayout(new BorderLayout());
     add(seqCanvas, BorderLayout.CENTER);
 
-    this.ap = ap;
+    this.ap = alignPanel;
 
-    if (!av.isDataset())
+    if (!viewport.isDataset())
     {
       addMouseMotionListener(this);
       addMouseListener(this);
       addMouseWheelListener(this);
-      ssm = av.getStructureSelectionManager();
+      ssm = viewport.getStructureSelectionManager();
       ssm.addStructureViewerListener(this);
       ssm.addSelectionListener(this);
     }
@@ -215,8 +211,8 @@ public class SeqPanel extends JPanel implements MouseListener,
               + hgap + seqCanvas.getAnnotationHeight();
 
       int y = evt.getY();
-      y -= hgap;
-      x = Math.max(0, x - seqCanvas.labelWidthWest);
+      y = Math.max(0, y - hgap);
+      x = Math.max(0, x - seqCanvas.getLabelWidthWest());
 
       int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());
       if (cwidth < 1)
@@ -228,8 +224,8 @@ public class SeqPanel extends JPanel implements MouseListener,
       wrappedBlock += startRes / cwidth;
       // allow for wrapped view scrolled right (possible from Overview)
       int startOffset = startRes % cwidth;
-      res = wrappedBlock * cwidth
-              + Math.min(cwidth - 1, startOffset + x / av.getCharWidth());
+      res = wrappedBlock * cwidth + startOffset
+              + +Math.min(cwidth - 1, x / av.getCharWidth());
     }
     else
     {
@@ -275,15 +271,14 @@ public class SeqPanel extends JPanel implements MouseListener,
 
       y -= hgap;
 
-      seq = Math.min((y % cHeight) / av.getCharHeight(), av.getAlignment()
-              .getHeight() - 1);
+      seq = Math.min((y % cHeight) / av.getCharHeight(),
+              av.getAlignment().getHeight() - 1);
     }
     else
     {
-      seq = Math.min((y / av.getCharHeight())
-              + av.getRanges().getStartSeq(),
-              av
-              .getAlignment().getHeight() - 1);
+      seq = Math.min(
+              (y / av.getCharHeight()) + av.getRanges().getStartSeq(),
+              av.getAlignment().getHeight() - 1);
     }
 
     return seq;
@@ -300,8 +295,8 @@ public class SeqPanel extends JPanel implements MouseListener,
       if (editCommand != null && editCommand.getSize() > 0)
       {
         ap.alignFrame.addHistoryItem(editCommand);
-        av.firePropertyChange("alignment", null, av.getAlignment()
-                .getSequences());
+        av.firePropertyChange("alignment", null,
+                av.getAlignment().getSequences());
       }
     } finally
     {
@@ -359,8 +354,7 @@ public class SeqPanel extends JPanel implements MouseListener,
 
     HiddenColumns hidden = av.getAlignment().getHiddenColumns();
 
-    if (av.hasHiddenColumns()
- && !hidden.isVisible(seqCanvas.cursorX))
+    if (av.hasHiddenColumns() && !hidden.isVisible(seqCanvas.cursorX))
     {
       int original = seqCanvas.cursorX - dx;
       int maxWidth = av.getAlignment().getWidth();
@@ -486,7 +480,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       av.setSelectionGroup(sg);
     }
 
-    ap.paintAlignment(false);
+    ap.paintAlignment(false, false);
     av.sendSelection();
   }
 
@@ -721,10 +715,12 @@ public class SeqPanel extends JPanel implements MouseListener,
   }
 
   /**
-   * DOCUMENT ME!
+   * Action on mouse movement is to update the status bar to show the current
+   * sequence position, and (if features are shown) to show any features at the
+   * position in a tooltip. Does nothing if the mouse move does not change
+   * residue position.
    * 
    * @param evt
-   *          DOCUMENT ME!
    */
   @Override
   public void mouseMoved(MouseEvent evt)
@@ -737,7 +733,8 @@ public class SeqPanel extends JPanel implements MouseListener,
     }
 
     final int column = findColumn(evt);
-    int seq = findSeq(evt);
+    final int seq = findSeq(evt);
+
     if (column < 0 || seq < 0 || seq >= av.getAlignment().getHeight())
     {
       lastMouseSeq = -1;
@@ -804,7 +801,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       List<SequenceFeature> features = ap.getFeatureRenderer()
               .findFeaturesAtColumn(sequence, column + 1);
       seqARep.appendFeatures(tooltipText, pos, features,
-              this.ap.getSeqPanel().seqCanvas.fr.getMinMax());
+              this.ap.getSeqPanel().seqCanvas.fr);
     }
     if (tooltipText.length() == 6) // <html>
     {
@@ -813,6 +810,11 @@ public class SeqPanel extends JPanel implements MouseListener,
     }
     else
     {
+      if (tooltipText.length() > MAX_TOOLTIP_LENGTH) // constant
+      {
+        tooltipText.setLength(MAX_TOOLTIP_LENGTH);
+        tooltipText.append("...");
+      }
       String textString = tooltipText.toString();
       if (lastTooltip == null || !lastTooltip.equals(textString))
       {
@@ -840,8 +842,9 @@ public class SeqPanel extends JPanel implements MouseListener,
     Point p = lastp;
     if (!event.isShiftDown() || p == null)
     {
-      p = (tooltipText != null && tooltipText.length() > 6) ? new Point(
-              event.getX() + wdth, event.getY() - 20) : null;
+      p = (tooltipText != null && tooltipText.length() > 6)
+              ? new Point(event.getX() + wdth, event.getY() - 20)
+              : null;
     }
     /*
      * TODO: try to modify position region is not obcured by tooltip
@@ -853,11 +856,12 @@ public class SeqPanel extends JPanel implements MouseListener,
 
   /**
    * set when the current UI interaction has resulted in a change that requires
-   * overview shading to be recalculated. this could be changed to something
-   * more expressive that indicates what actually has changed, so selective
-   * redraws can be applied
+   * shading in overviews and structures to be recalculated. this could be
+   * changed to a something more expressive that indicates what actually has
+   * changed, so selective redraws can be applied (ie. only structures, only
+   * overview, etc)
    */
-  private boolean needOverviewUpdate = false; // TODO: refactor to avcontroller
+  private boolean updateOverviewAndStructs = false; // TODO: refactor to avcontroller
 
   /**
    * set if av.getSelectionGroup() refers to a group that is defined on the
@@ -939,9 +943,9 @@ public class SeqPanel extends JPanel implements MouseListener,
       }
       else
       {
-        residue = "X".equalsIgnoreCase(displayChar) ? "X" : ("*"
-                .equals(displayChar) ? "STOP"
-                : ResidueProperties.aa2Triplet.get(displayChar));
+        residue = "X".equalsIgnoreCase(displayChar) ? "X"
+                : ("*".equals(displayChar) ? "STOP"
+                        : ResidueProperties.aa2Triplet.get(displayChar));
       }
       text.append(" ").append(nucleotide ? "Nucleotide" : "Residue")
               .append(": ").append(residue == null ? displayChar : residue);
@@ -998,8 +1002,8 @@ public class SeqPanel extends JPanel implements MouseListener,
       int oldWidth = av.getCharWidth();
 
       // Which is bigger, left-right or up-down?
-      if (Math.abs(evt.getY() - lastMousePress.getY()) > Math.abs(evt
-              .getX() - lastMousePress.getX()))
+      if (Math.abs(evt.getY() - lastMousePress.getY()) > Math
+              .abs(evt.getX() - lastMousePress.getX()))
       {
         /*
          * on drag up or down, decrement or increment font size
@@ -1058,7 +1062,7 @@ public class SeqPanel extends JPanel implements MouseListener,
         }
         if (newWidth > 0)
         {
-          ap.paintAlignment(false);
+          ap.paintAlignment(false, false);
           if (copyChanges)
           {
             /*
@@ -1112,7 +1116,7 @@ public class SeqPanel extends JPanel implements MouseListener,
     }
 
     mouseDragging = true;
-    if (scrollThread != null)
+    if ((scrollThread != null) && (scrollThread.isRunning()))
     {
       scrollThread.setEvent(evt);
     }
@@ -1159,8 +1163,9 @@ public class SeqPanel extends JPanel implements MouseListener,
       }
       if (editCommand == null)
       {
-        editCommand = new EditCommand(MessageManager.formatMessage(
-                "label.edit_params", new String[] { label }));
+        editCommand = new EditCommand(MessageManager
+                .formatMessage("label.edit_params", new String[]
+                { label }));
       }
     }
 
@@ -1177,9 +1182,8 @@ public class SeqPanel extends JPanel implements MouseListener,
     ap.alignFrame.statusBar.setText(message.toString());
 
     // Are we editing within a selection group?
-    if (groupEditing
-            || (sg != null && sg.getSequences(av.getHiddenRepSequences())
-                    .contains(seq)))
+    if (groupEditing || (sg != null
+            && sg.getSequences(av.getHiddenRepSequences()).contains(seq)))
     {
       fixedColumns = true;
 
@@ -1275,7 +1279,7 @@ public class SeqPanel extends JPanel implements MouseListener,
         // Find the next gap before the end
         // of the visible region boundary
         boolean blank = false;
-        for (fixedRight = fixedRight; fixedRight > lastres; fixedRight--)
+        for (; fixedRight > lastres; fixedRight--)
         {
           blank = true;
 
@@ -1367,8 +1371,8 @@ public class SeqPanel extends JPanel implements MouseListener,
         }
         else
         {
-          appendEdit(Action.INSERT_GAP, groupSeqs, startres, startres
-                  - lastres);
+          appendEdit(Action.INSERT_GAP, groupSeqs, startres,
+                  startres - lastres);
         }
       }
       else
@@ -1383,8 +1387,8 @@ public class SeqPanel extends JPanel implements MouseListener,
         }
         else
         {
-          appendEdit(Action.DELETE_GAP, groupSeqs, startres, lastres
-                  - startres);
+          appendEdit(Action.DELETE_GAP, groupSeqs, startres,
+                  lastres - startres);
         }
 
       }
@@ -1538,9 +1542,9 @@ public class SeqPanel extends JPanel implements MouseListener,
       oldSeq = 0;
     }
 
-    if (scrollThread != null)
+    if ((scrollThread != null) && (scrollThread.isRunning()))
     {
-      scrollThread.running = false;
+      scrollThread.stopScrolling();
       scrollThread = null;
     }
   }
@@ -1559,7 +1563,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       return;
     }
 
-    if (mouseDragging)
+    if (mouseDragging && scrollThread == null)
     {
       scrollThread = new ScrollThread();
     }
@@ -1643,7 +1647,13 @@ public class SeqPanel extends JPanel implements MouseListener,
         av.getRanges().scrollUp(true);
       }
     }
-    // TODO Update tooltip for new position.
+
+    /*
+     * update status bar and tooltip for new position
+     * (need to synthesize a mouse movement to refresh tooltip)
+     */
+    mouseMoved(e);
+    ToolTipManager.sharedInstance().mouseMoved(e);
   }
 
   /**
@@ -1657,14 +1667,15 @@ public class SeqPanel extends JPanel implements MouseListener,
     final int res = findColumn(evt);
     final int seq = findSeq(evt);
     oldSeq = seq;
-    needOverviewUpdate = false;
+    updateOverviewAndStructs = false;
 
     startWrapBlock = wrappedBlock;
 
     if (av.getWrapAlignment() && seq > av.getAlignment().getHeight())
     {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
-              .getString("label.cannot_edit_annotations_in_wrapped_view"),
+      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+              MessageManager.getString(
+                      "label.cannot_edit_annotations_in_wrapped_view"),
               MessageManager.getString("label.wrapped_view_no_edit"),
               JvOptionPane.WARNING_MESSAGE);
       return;
@@ -1721,43 +1732,53 @@ public class SeqPanel extends JPanel implements MouseListener,
 
     if (stretchGroup == null)
     {
-      // Only if left mouse button do we want to change group sizes
+      createStretchGroup(res, sequence);
+    }
 
-      // define a new group here
-      SequenceGroup sg = new SequenceGroup();
-      sg.setStartRes(res);
-      sg.setEndRes(res);
-      sg.addSequence(sequence, false);
-      av.setSelectionGroup(sg);
-      stretchGroup = sg;
+    if (stretchGroup != null)
+    {
+      stretchGroup.addPropertyChangeListener(seqCanvas);
+    }
 
-      if (av.getConservationSelected())
-      {
-        SliderPanel.setConservationSlider(ap, av.getResidueShading(),
-                ap.getViewName());
-      }
+    seqCanvas.repaint();
+  }
 
-      if (av.getAbovePIDThreshold())
-      {
-        SliderPanel.setPIDSliderSource(ap, av.getResidueShading(),
-                ap.getViewName());
-      }
-      // TODO: stretchGroup will always be not null. Is this a merge error ?
-      if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))
-      {
-        // Edit end res position of selected group
-        changeEndRes = true;
-      }
-      else if ((stretchGroup != null)
-              && (stretchGroup.getStartRes() == res))
-      {
-        // Edit end res position of selected group
-        changeStartRes = true;
-      }
-      stretchGroup.getWidth();
+  private void createStretchGroup(int res, SequenceI sequence)
+  {
+    // Only if left mouse button do we want to change group sizes
+    // define a new group here
+    SequenceGroup sg = new SequenceGroup();
+    sg.setStartRes(res);
+    sg.setEndRes(res);
+    sg.addSequence(sequence, false);
+    av.setSelectionGroup(sg);
+    stretchGroup = sg;
+
+    if (av.getConservationSelected())
+    {
+      SliderPanel.setConservationSlider(ap, av.getResidueShading(),
+              ap.getViewName());
     }
 
-    seqCanvas.repaint();
+    if (av.getAbovePIDThreshold())
+    {
+      SliderPanel.setPIDSliderSource(ap, av.getResidueShading(),
+              ap.getViewName());
+    }
+    // TODO: stretchGroup will always be not null. Is this a merge error ?
+    // or is there a threading issue here?
+    if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))
+    {
+      // Edit end res position of selected group
+      changeEndRes = true;
+    }
+    else if ((stretchGroup != null) && (stretchGroup.getStartRes() == res))
+    {
+      // Edit end res position of selected group
+      changeStartRes = true;
+    }
+    stretchGroup.getWidth();
+
   }
 
   /**
@@ -1772,21 +1793,10 @@ public class SeqPanel extends JPanel implements MouseListener,
     final int column = findColumn(evt);
     final int seq = findSeq(evt);
     SequenceI sequence = av.getAlignment().getSequenceAt(seq);
-    List<SequenceFeature> allFeatures = ap.getFeatureRenderer()
+    List<SequenceFeature> features = ap.getFeatureRenderer()
             .findFeaturesAtColumn(sequence, column + 1);
-    List<String> links = new ArrayList<>();
-    for (SequenceFeature sf : allFeatures)
-    {
-      if (sf.links != null)
-      {
-        for (String link : sf.links)
-        {
-          links.add(link);
-        }
-      }
-    }
 
-    PopupMenu pop = new PopupMenu(ap, null, links);
+    PopupMenu pop = new PopupMenu(ap, null, features);
     pop.show(this, evt.getX(), evt.getY());
   }
 
@@ -1805,17 +1815,21 @@ public class SeqPanel extends JPanel implements MouseListener,
     {
       return;
     }
+
+    stretchGroup.removePropertyChangeListener(seqCanvas);
+
     // always do this - annotation has own state
     // but defer colourscheme update until hidden sequences are passed in
     boolean vischange = stretchGroup.recalcConservation(true);
-    needOverviewUpdate |= vischange && av.isSelectionDefinedGroup()
+    updateOverviewAndStructs |= vischange && av.isSelectionDefinedGroup()
             && afterDrag;
     if (stretchGroup.cs != null)
     {
       stretchGroup.cs.alignmentChanged(stretchGroup,
               av.getHiddenRepSequences());
 
-      ResidueShaderI groupColourScheme = stretchGroup.getGroupColourScheme();
+      ResidueShaderI groupColourScheme = stretchGroup
+              .getGroupColourScheme();
       String name = stretchGroup.getName();
       if (stretchGroup.cs.conservationApplied())
       {
@@ -1827,8 +1841,10 @@ public class SeqPanel extends JPanel implements MouseListener,
       }
     }
     PaintRefresher.Refresh(this, av.getSequenceSetId());
-    ap.paintAlignment(needOverviewUpdate);
-    needOverviewUpdate = false;
+    // TODO: structure colours only need updating if stretchGroup used to or now
+    // does contain sequences with structure views
+    ap.paintAlignment(updateOverviewAndStructs, updateOverviewAndStructs);
+    updateOverviewAndStructs = false;
     changeEndRes = false;
     changeStartRes = false;
     stretchGroup = null;
@@ -1882,7 +1898,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       if (res > (stretchGroup.getStartRes() - 1))
       {
         stretchGroup.setEndRes(res);
-        needOverviewUpdate |= av.isSelectionDefinedGroup();
+        updateOverviewAndStructs |= av.isSelectionDefinedGroup();
       }
     }
     else if (changeStartRes)
@@ -1890,7 +1906,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       if (res < (stretchGroup.getEndRes() + 1))
       {
         stretchGroup.setStartRes(res);
-        needOverviewUpdate |= av.isSelectionDefinedGroup();
+        updateOverviewAndStructs |= av.isSelectionDefinedGroup();
       }
     }
 
@@ -1924,7 +1940,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       if (stretchGroup.getSequences(null).contains(nextSeq))
       {
         stretchGroup.deleteSequence(seq, false);
-        needOverviewUpdate |= av.isSelectionDefinedGroup();
+        updateOverviewAndStructs |= av.isSelectionDefinedGroup();
       }
       else
       {
@@ -1934,7 +1950,7 @@ public class SeqPanel extends JPanel implements MouseListener,
         }
 
         stretchGroup.addSequence(nextSeq, false);
-        needOverviewUpdate |= av.isSelectionDefinedGroup();
+        updateOverviewAndStructs |= av.isSelectionDefinedGroup();
       }
     }
 
@@ -1945,21 +1961,19 @@ public class SeqPanel extends JPanel implements MouseListener,
 
     mouseDragging = true;
 
-    if (scrollThread != null)
+    if ((scrollThread != null) && (scrollThread.isRunning()))
     {
       scrollThread.setEvent(evt);
     }
-
-    seqCanvas.repaint();
   }
 
   void scrollCanvas(MouseEvent evt)
   {
     if (evt == null)
     {
-      if (scrollThread != null)
+      if ((scrollThread != null) && (scrollThread.isRunning()))
       {
-        scrollThread.running = false;
+        scrollThread.stopScrolling();
         scrollThread = null;
       }
       mouseDragging = false;
@@ -1982,7 +1996,7 @@ public class SeqPanel extends JPanel implements MouseListener,
   {
     MouseEvent evt;
 
-    boolean running = false;
+    private volatile boolean threadRunning = true;
 
     public ScrollThread()
     {
@@ -1996,38 +2010,40 @@ public class SeqPanel extends JPanel implements MouseListener,
 
     public void stopScrolling()
     {
-      running = false;
+      threadRunning = false;
+    }
+
+    public boolean isRunning()
+    {
+      return threadRunning;
     }
 
     @Override
     public void run()
     {
-      running = true;
-
-      while (running)
+      while (threadRunning)
       {
         if (evt != null)
         {
           if (mouseDragging && (evt.getY() < 0)
                   && (av.getRanges().getStartSeq() > 0))
           {
-            running = av.getRanges().scrollUp(true);
+            av.getRanges().scrollUp(true);
           }
 
-          if (mouseDragging && (evt.getY() >= getHeight())
-                  && (av.getAlignment().getHeight() > av.getRanges()
-                          .getEndSeq()))
+          if (mouseDragging && (evt.getY() >= getHeight()) && (av
+                  .getAlignment().getHeight() > av.getRanges().getEndSeq()))
           {
-            running = av.getRanges().scrollUp(false);
+            av.getRanges().scrollUp(false);
           }
 
           if (mouseDragging && (evt.getX() < 0))
           {
-            running = av.getRanges().scrollRight(false);
+            av.getRanges().scrollRight(false);
           }
           else if (mouseDragging && (evt.getX() >= getWidth()))
           {
-            running = av.getRanges().scrollRight(true);
+            av.getRanges().scrollRight(true);
           }
         }
 
@@ -2052,8 +2068,10 @@ public class SeqPanel extends JPanel implements MouseListener,
     // handles selection messages...
     // TODO: extend config options to allow user to control if selections may be
     // shared between viewports.
-    boolean iSentTheSelection = (av == source || (source instanceof AlignViewport && ((AlignmentViewport) source)
-            .getSequenceSetId().equals(av.getSequenceSetId())));
+    boolean iSentTheSelection = (av == source
+            || (source instanceof AlignViewport
+                    && ((AlignmentViewport) source).getSequenceSetId()
+                            .equals(av.getSequenceSetId())));
 
     if (iSentTheSelection)
     {
@@ -2155,8 +2173,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       repaint = true;
     }
 
-    if (copycolsel
-            && av.hasHiddenColumns()
+    if (copycolsel && av.hasHiddenColumns()
             && (av.getAlignment().getHiddenColumns() == null))
     {
       System.err.println("Bad things");