JAL-2349 JAL-3855 highlight residues associated with elements under mouse - Jmol...
[jalview.git] / src / jalview / gui / AnnotationPanel.java
index d80c749..d31e52d 100755 (executable)
@@ -56,6 +56,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.ContactListI;
+import jalview.datamodel.ContactRange;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.SequenceI;
 import jalview.gui.JalviewColourChooser.ColourChooserListener;
@@ -82,7 +83,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
 {
   enum DragMode
   {
-    Select, Resize, Undefined
+    Select, Resize, Undefined, MatrixSelect
   };
 
   String HELIX = MessageManager.getString("label.helix");
@@ -132,6 +133,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
 
   int mouseDragLastY = -1;
 
+  int firstDragX = -1;
+
+  int firstDragY = -1;
+
   DragMode dragMode = DragMode.Undefined;
 
   boolean mouseDragging = false;
@@ -539,8 +544,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
      */
     int height = 0;
     activeRow = -1;
-
+    int yOffset = 0;
+    // todo could reuse getRowIndexAndOffset ?
     final int y = evt.getY();
+
     for (int i = 0; i < aa.length; i++)
     {
       if (aa[i].visible)
@@ -560,6 +567,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
            * we have clicked on a resizable graph annotation
            */
           graphStretch = i;
+          yOffset = height - y;
         }
         break;
       }
@@ -575,7 +583,44 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       return;
     }
 
-    ap.getScalePanel().mousePressed(evt);
+    if (graphStretch != -1)
+    {
+
+      if (aa[graphStretch].graph == AlignmentAnnotation.CUSTOMRENDERER)
+      {
+        if (evt.isAltDown() || evt.isAltGraphDown())
+        {
+          dragMode = DragMode.MatrixSelect;
+          firstDragX = mouseDragLastX;
+          firstDragY = mouseDragLastY;
+        }
+        else
+        {
+          int currentX = getColumnForXPos(evt.getX());
+          ContactListI forCurrentX = av.getContactList(aa[graphStretch],
+                  currentX);
+          if (forCurrentX != null)
+          {
+            ContactGeometry cXcgeom = new ContactGeometry(forCurrentX,
+                    aa[graphStretch].graphHeight);
+            ContactGeometry.contactInterval cXci = cXcgeom.mapFor(yOffset,
+                    yOffset);
+            int fr, to;
+            fr = Math.min(cXci.cStart, cXci.cEnd);
+            to = Math.max(cXci.cStart, cXci.cEnd);
+            for (int c = fr; c <= to; c++)
+            {
+              av.getColumnSelection().addElement(c);
+            }
+            av.getColumnSelection().addElement(currentX);
+          }
+        }
+      }
+    }
+    else
+    {
+      ap.getScalePanel().mousePressed(evt);
+    }
   }
 
   /**
@@ -635,9 +680,15 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   @Override
   public void mouseReleased(MouseEvent evt)
   {
+    if (dragMode == DragMode.MatrixSelect)
+    {
+      matrixSelectRange(evt);
+    }
     graphStretch = -1;
     mouseDragLastX = -1;
     mouseDragLastY = -1;
+    firstDragX = -1;
+    firstDragY = -1;
     mouseDragging = false;
     if (dragMode == DragMode.Resize)
     {
@@ -725,10 +776,25 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
          * mostly vertical drag
          */
         dragMode = DragMode.Resize;
+
+        /*
+         * but could also be a matrix drag
+         */
+        if ((evt.isAltDown() || evt.isAltGraphDown()) && (av.getAlignment()
+                .getAlignmentAnnotation()[graphStretch].graph == AlignmentAnnotation.CUSTOMRENDERER))
+        {
+          /*
+           * dragging in a matrix
+           */
+          dragMode = DragMode.MatrixSelect;
+          firstDragX = mouseDragLastX;
+          firstDragY = mouseDragLastY;
+        }
       }
     }
 
     if (dragMode == DragMode.Undefined)
+
     {
       /*
        * drag is diagonal - defer deciding whether to
@@ -755,6 +821,15 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
           ap.paintAlignment(false, false);
         }
       }
+      else if (dragMode == DragMode.MatrixSelect)
+      {
+        /*
+         * TODO draw a rubber band for range
+         */
+        mouseDragLastX = x;
+        mouseDragLastY = y;
+        ap.paintAlignment(false, false);
+      }
       else
       {
         /*
@@ -770,6 +845,104 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     }
   }
 
+  public void matrixSelectRange(MouseEvent evt)
+  {
+    /*
+     * get geometry of drag
+     */
+    int fromY = Math.min(firstDragY, evt.getY());
+    int toY = Math.max(firstDragY, evt.getY());
+    int fromX = Math.min(firstDragX, evt.getX());
+    int toX = Math.max(firstDragX, evt.getX());
+
+    int deltaY = toY - fromY;
+    int deltaX = toX - fromX;
+
+    int[] rowIndex = getRowIndexAndOffset(fromY,
+            av.getAlignment().getAlignmentAnnotation());
+    int[] toRowIndex = getRowIndexAndOffset(toY,
+            av.getAlignment().getAlignmentAnnotation());
+
+    if (rowIndex == null || toRowIndex == null)
+    {
+      System.out.println("Drag out of range. needs to be clipped");
+
+    }
+    if (rowIndex[0] != toRowIndex[0])
+    {
+      System.out.println("Drag went to another row. needs to be clipped");
+    }
+
+    // rectangular selection on matrix style annotation
+    AlignmentAnnotation cma = av.getAlignment()
+            .getAlignmentAnnotation()[rowIndex[0]];
+
+    int lastX = getColumnForXPos(fromX);
+    int currentX = getColumnForXPos(toX);
+    int fromXc = Math.min(lastX, currentX);
+    int toXc = Math.max(lastX, currentX);
+    ContactListI forFromX = av.getContactList(cma, fromXc);
+    ContactListI forToX = av.getContactList(cma, toXc);
+
+    if (forFromX != null && forToX != null)
+    {
+      ContactGeometry lastXcgeom = new ContactGeometry(forFromX,
+              cma.graphHeight);
+      ContactGeometry.contactInterval lastXci = lastXcgeom
+              .mapFor(rowIndex[1], rowIndex[1] - deltaY);
+
+      ContactGeometry cXcgeom = new ContactGeometry(forToX,
+              cma.graphHeight);
+      ContactGeometry.contactInterval cXci = cXcgeom.mapFor(rowIndex[1],
+              rowIndex[1] - deltaY);
+
+      // mark rectangular region formed by drag
+      System.err.println("Matrix Selection from last(" + fromXc + ",["
+              + lastXci.cStart + "," + lastXci.cEnd + "]) to cur(" + toXc
+              + ",[" + cXci.cStart + "," + cXci.cEnd + "])");
+      int fr, to;
+      fr = Math.min(lastXci.cStart, lastXci.cEnd);
+      to = Math.max(lastXci.cStart, lastXci.cEnd);
+      System.err.println("Marking " + fr + " to " + to);
+      for (int c = fr; c <= to; c++)
+      {
+        if (cma.sequenceRef != null)
+        {
+          int col = cma.sequenceRef.findIndex(c);
+          av.getColumnSelection().addElement(col);
+        }
+        else
+        {
+          av.getColumnSelection().addElement(c);
+        }
+      }
+      fr = Math.min(cXci.cStart, cXci.cEnd);
+      to = Math.max(cXci.cStart, cXci.cEnd);
+      System.err.println("Marking " + fr + " to " + to);
+      for (int c = fr; c <= to; c++)
+      {
+        if (cma.sequenceRef != null)
+        {
+          int col = cma.sequenceRef.findIndex(c);
+          av.getColumnSelection().addElement(col);
+        }
+        else
+        {
+          av.getColumnSelection().addElement(c);
+        }
+      }
+      fr = Math.min(lastX, currentX);
+      to = Math.max(lastX, currentX);
+
+      System.err.println("Marking " + fr + " to " + to);
+      for (int c = fr; c <= to; c++)
+      {
+        av.getColumnSelection().addElement(c);
+      }
+    }
+
+  }
+
   /**
    * Constructs the tooltip, and constructs and displays a status message, for
    * the current mouse position
@@ -790,21 +963,14 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       return;
     }
 
-    int column = (evt.getX() / av.getCharWidth())
-            + av.getRanges().getStartRes();
-    column = Math.min(column, av.getRanges().getEndRes());
-
-    if (av.hasHiddenColumns())
-    {
-      column = av.getAlignment().getHiddenColumns()
-              .visibleToAbsoluteColumn(column);
-    }
+    int column = getColumnForXPos(evt.getX());
 
     AlignmentAnnotation ann = aa[row];
     if (row > -1 && ann.annotations != null
             && column < ann.annotations.length)
     {
-      String toolTip = buildToolTip(ann, column, aa, rowAndOffset[1], av);
+      String toolTip = buildToolTip(ann, column, aa, rowAndOffset[1], av,
+              ap);
       setToolTipText(toolTip == null ? null
               : JvSwingUtils.wrapTooltip(true, toolTip));
       String msg = getStatusMessage(av.getAlignment(), column, ann,
@@ -818,6 +984,19 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     }
   }
 
+  private int getColumnForXPos(int x)
+  {
+    int column = (x / av.getCharWidth()) + av.getRanges().getStartRes();
+    column = Math.min(column, av.getRanges().getEndRes());
+
+    if (av.hasHiddenColumns())
+    {
+      column = av.getAlignment().getHiddenColumns()
+              .visibleToAbsoluteColumn(column);
+    }
+    return column;
+  }
+
   /**
    * Answers the index in the annotations array of the visible annotation at the
    * given y position. This is done by adding the heights of visible annotations
@@ -840,15 +1019,14 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
   static int[] getRowIndexAndOffset(int yPos, AlignmentAnnotation[] aa)
   {
     int[] res = new int[2];
+    res[0] = -1;
+    res[1] = 0;
     if (aa == null)
     {
-      res[0] = -1;
-      res[1] = 0;
       return res;
     }
     int row = -1;
     int height = 0, lheight = 0;
-
     for (int i = 0; i < aa.length; i++)
     {
       if (aa[i].visible)
@@ -861,7 +1039,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       {
         row = i;
         res[0] = row;
-        res[1] = yPos - lheight;
+        res[1] = height - yPos;
         break;
       }
     }
@@ -879,7 +1057,8 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
    * @param rowAndOffset
    */
   static String buildToolTip(AlignmentAnnotation ann, int column,
-          AlignmentAnnotation[] anns, int rowAndOffset, AlignViewportI av)
+          AlignmentAnnotation[] anns, int rowAndOffset, AlignViewportI av,
+          AlignmentPanel ap)
   {
     String tooltip = null;
     if (ann.graphGroup > -1)
@@ -918,9 +1097,16 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       if (clist != null)
       {
         ContactGeometry cgeom = new ContactGeometry(clist, ann.graphHeight);
-        ContactGeometry.contactInterval ci = cgeom.mapFor(rowAndOffset,
-                rowAndOffset);
-        tooltip += "Contact from " + ci.cStart + " to " + ci.cEnd;
+        ContactGeometry.contactInterval ci = cgeom.mapFor(rowAndOffset);
+        ContactRange cr = clist.getRangeFor(ci.cStart, ci.cEnd);
+        tooltip = "Contact from " + clist.getPosition() + ", [" + ci.cStart
+                + " - " + ci.cEnd + "]" + "<br/>Mean:" + cr.getMean();
+        int col = ann.sequenceRef.findPosition(column);
+        ap.getStructureSelectionManager()
+                .highlightPositionsOn(ann.sequenceRef, new int[][]
+                { new int[] { col, col },
+                    new int[]
+                    { ci.cStart, ci.cEnd } }, null);
       }
     }
     return tooltip;
@@ -1275,6 +1461,17 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     {
       fadedImage = oldFaded;
     }
+    if (dragMode == DragMode.MatrixSelect)
+    {
+      g.setColor(Color.yellow);
+      g.drawRect(Math.min(firstDragX, mouseDragLastX),
+              Math.min(firstDragY, mouseDragLastY),
+              Math.max(firstDragX, mouseDragLastX)
+                      - Math.min(firstDragX, mouseDragLastX),
+              Math.max(firstDragY, mouseDragLastY)
+                      - Math.min(firstDragY, mouseDragLastY));
+
+    }
   }
 
   @Override