Merge branch 'features/JAL-2446NCList' into features/JAL-2609fastPaintWrapped
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 1 Aug 2017 09:58:32 +0000 (11:58 +0200)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 1 Aug 2017 09:58:32 +0000 (11:58 +0200)
15 files changed:
examples/exampleFeatures.txt
src/jalview/appletgui/FeatureColourChooser.java
src/jalview/appletgui/FeatureSettings.java
src/jalview/appletgui/OverviewPanel.java
src/jalview/appletgui/PaintRefresher.java
src/jalview/datamodel/HiddenSequences.java
src/jalview/gui/FeatureColourChooser.java
src/jalview/gui/IdPanel.java
src/jalview/gui/OverviewPanel.java
src/jalview/viewmodel/OverviewDimensions.java
src/jalview/viewmodel/OverviewDimensionsHideHidden.java
src/jalview/viewmodel/OverviewDimensionsShowHidden.java
test/jalview/io/FeaturesFileTest.java
test/jalview/viewmodel/OverviewDimensionsHideHiddenTest.java
test/jalview/viewmodel/OverviewDimensionsShowHiddenTest.java

index 83dc4b1..cc159d1 100755 (executable)
@@ -1,5 +1,5 @@
-ST-TURN-IIL    blue|255,0,255|absolute|20.0|95.0|below|66.0
-GAMMA-TURN-CLASSIC     lightGray|0,255,255|20.0|95.0|below|66.0
+ST-TURN-IIL    blue
+GAMMA-TURN-CLASSIC     lightGray
 BETA-TURN-IR   9a6a94
 BETA-TURN-IL   d6a6ca
 BETA-BULGE     1dc451
@@ -24,6 +24,7 @@ BETA-TURN-IIR c79792
 PHOSPHORYLATION (T)    c88395
 BETA-TURN-IIL  8b5b50
 ST-MOTIF       ac25a1
+kdHydrophobicity       ccffcc|333300|-3.9|4.5|above|-2.0
 
 STARTGROUP     uniprot
 <html><a href="http://pfam.xfam.org/family/PF00111">Pfam family</a></html>     FER_CAPAA       -1      0       0       Pfam
@@ -310,3 +311,54 @@ NEST-RL    FER1_MAIZE      -1      124     126     NEST-RL
 NEST-RL        FER1_MAIZE      -1      241     243     NEST-RL
 NEST-RL        FER1_MAIZE      -1      292     294     NEST-RL
 ENDGROUP       s3dm
+
+STARTGROUP     kd
+A      Q93XJ9_SOLTU    -1      48      48      kdHydrophobicity        1.8
+A      Q93XJ9_SOLTU    -1      49      49      kdHydrophobicity        -0.8
+A      Q93XJ9_SOLTU    -1      50      50      kdHydrophobicity        -1.3
+A      Q93XJ9_SOLTU    -1      51      51      kdHydrophobicity        -3.9
+A      Q93XJ9_SOLTU    -1      52      52      kdHydrophobicity        4.2
+A      Q93XJ9_SOLTU    -1      53      53      kdHydrophobicity        -3.9
+A      Q93XJ9_SOLTU    -1      54      54      kdHydrophobicity        3.8
+A      Q93XJ9_SOLTU    -1      55      55      kdHydrophobicity        4.5
+A      Q93XJ9_SOLTU    -1      56      56      kdHydrophobicity        -0.7
+A      Q93XJ9_SOLTU    -1      57      57      kdHydrophobicity        -1.6
+A      Q93XJ9_SOLTU    -1      58      58      kdHydrophobicity        -3.5
+A      Q93XJ9_SOLTU    -1      59      59      kdHydrophobicity        -0.4
+A      Q93XJ9_SOLTU    -1      60      60      kdHydrophobicity        -1.6
+A      Q93XJ9_SOLTU    -1      61      61      kdHydrophobicity        4.5
+A      Q93XJ9_SOLTU    -1      62      62      kdHydrophobicity        -3.5
+A      Q93XJ9_SOLTU    -1      63      63      kdHydrophobicity        2.8
+A      FER1_SPIOL      -1      51      51      kdHydrophobicity        1.8
+A      FER1_SPIOL      -1      52      52      kdHydrophobicity        1.8
+A      FER1_SPIOL      -1      53      53      kdHydrophobicity        -1.3
+A      FER1_SPIOL      -1      54      54      kdHydrophobicity        -3.9
+A      FER1_SPIOL      -1      55      55      kdHydrophobicity        4.2
+A      FER1_SPIOL      -1      56      56      kdHydrophobicity        -0.7
+A      FER1_SPIOL      -1      57      57      kdHydrophobicity        3.8
+A      FER1_SPIOL      -1      58      58      kdHydrophobicity        4.2
+A      FER1_SPIOL      -1      59      59      kdHydrophobicity        -0.7
+A      FER1_SPIOL      -1      60      60      kdHydrophobicity        -1.6
+A      FER1_SPIOL      -1      61      61      kdHydrophobicity        -0.7
+A      FER1_SPIOL      -1      62      62      kdHydrophobicity        -0.4
+A      FER1_SPIOL      -1      63      63      kdHydrophobicity        -3.5
+A      FER1_SPIOL      -1      64      64      kdHydrophobicity        4.2
+A      FER1_SPIOL      -1      65      65      kdHydrophobicity        -3.5
+A      FER1_SPIOL      -1      66      66      kdHydrophobicity        2.8
+C      FER1_MAIZE      -1      53      53      kdHydrophobicity        1.8
+C      FER1_MAIZE      -1      54      54      kdHydrophobicity        -0.7
+C      FER1_MAIZE      -1      55      55      kdHydrophobicity        -1.3
+C      FER1_MAIZE      -1      56      56      kdHydrophobicity        -3.5
+C      FER1_MAIZE      -1      57      57      kdHydrophobicity        4.2
+C      FER1_MAIZE      -1      58      58      kdHydrophobicity        -3.9
+C      FER1_MAIZE      -1      59      59      kdHydrophobicity        3.8
+C      FER1_MAIZE      -1      60      60      kdHydrophobicity        4.5
+C      FER1_MAIZE      -1      61      61      kdHydrophobicity        -0.7
+C      FER1_MAIZE      -1      62      62      kdHydrophobicity        -1.6
+C      FER1_MAIZE      -1      63      63      kdHydrophobicity        -3.5
+C      FER1_MAIZE      -1      64      64      kdHydrophobicity        -0.4
+C      FER1_MAIZE      -1      65      65      kdHydrophobicity        -3.5
+C      FER1_MAIZE      -1      66      66      kdHydrophobicity        4.2
+C      FER1_MAIZE      -1      67      67      kdHydrophobicity        -3.5
+C      FER1_MAIZE      -1      68      68      kdHydrophobicity        3.8
+ENDGROUP       kd
index 4075e8b..72fa982 100644 (file)
@@ -46,51 +46,81 @@ import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
-import java.util.Hashtable;
 
 public class FeatureColourChooser extends Panel implements ActionListener,
         AdjustmentListener, ItemListener, MouseListener
 {
-  JVDialog frame;
+  /*
+   * the absolute min-max range of a feature score is scaled to 
+   * 1000 positions on the colour threshold slider
+   */
+  private static final int SCALE_FACTOR_1K = 1000;
 
-  Frame owner;
+  private JVDialog frame;
 
-  FeatureRenderer fr;
+  private Frame owner;
 
-  FeatureSettings fs = null;
+  private FeatureRenderer fr;
 
-  // AlignmentPanel ap;
+  private FeatureSettings fs = null;
 
-  FeatureColourI cs;
+  private FeatureColourI cs;
 
-  FeatureColourI oldcs;
+  private FeatureColourI oldcs;
 
-  Hashtable oldgroupColours;
-
-  boolean adjusting = false;
+  private boolean adjusting = false;
 
   private float min, max;
 
-  String type = null;
+  private String type = null;
 
   private AlignFrame af = null;
 
-  public FeatureColourChooser(AlignFrame af, String type)
+  private Panel minColour = new Panel();
+
+  private Panel maxColour = new Panel();
+
+  private Choice threshold = new Choice();
+
+  private Scrollbar slider = new Scrollbar(Scrollbar.HORIZONTAL);
+
+  private TextField thresholdValue = new TextField(20);
+
+  private Checkbox thresholdIsMin = new Checkbox();
+
+  private Checkbox colourFromLabel = new Checkbox();
+
+  private GraphLine threshline;
+
+  /**
+   * Constructor given a context AlignFrame and a feature type. This is used
+   * when opening the graduated colour dialog from the Amend Feature dialog.
+   * 
+   * @param alignFrame
+   * @param featureType
+   */
+  public FeatureColourChooser(AlignFrame alignFrame, String featureType)
   {
-    this.af = af;
-    init(af.getSeqcanvas().getFeatureRenderer(), type);
+    this.af = alignFrame;
+    init(alignFrame.getSeqcanvas().getFeatureRenderer(), featureType);
   }
 
-  public FeatureColourChooser(FeatureSettings fsettings, String type)
+  /**
+   * Constructor given a context FeatureSettings and a feature type. This is
+   * used when opening the graduated colour dialog from Feature Settings.
+   * 
+   * @param fsettings
+   * @param featureType
+   */
+  public FeatureColourChooser(FeatureSettings fsettings, String featureType)
   {
     this.fs = fsettings;
-    init(fsettings.fr, type);
-    // this.ap = fsettings.ap;
+    init(fsettings.fr, featureType);
   }
 
-  private void init(FeatureRenderer frenderer, String type)
+  private void init(FeatureRenderer frenderer, String featureType)
   {
-    this.type = type;
+    this.type = featureType;
     fr = frenderer;
     float mm[] = fr.getMinMax().get(type)[0];
     min = mm[0];
@@ -130,7 +160,7 @@ public class FeatureColourChooser extends Panel implements ActionListener,
             : 0));
 
     adjusting = false;
-    changeColour();
+    changeColour(true);
     colourFromLabel.addItemListener(this);
     slider.addAdjustmentListener(this);
     slider.addMouseListener(this);
@@ -143,13 +173,12 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     frame.setVisible(true);
     if (frame.accept)
     {
-      changeColour();
+      changeColour(true);
     }
     else
     {
       // cancel
       reset();
-      PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId());
       frame.setVisible(false);
     }
   }
@@ -184,9 +213,13 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     thresholdIsMin.addItemListener(this);
 
     this.setLayout(new GridLayout(4, 1));
+    Panel jPanel1 = new Panel();
     jPanel1.setLayout(new FlowLayout());
+    Panel jPanel2 = new Panel();
     jPanel2.setLayout(new FlowLayout());
+    Panel jPanel3 = new Panel();
     jPanel3.setLayout(new GridLayout(1, 1));
+    Panel jPanel4 = new Panel();
     jPanel4.setLayout(new FlowLayout());
     jPanel1.setBackground(Color.white);
     jPanel2.setBackground(Color.white);
@@ -234,32 +267,6 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     this.add(jPanel4);// , java.awt.BorderLayout.CENTER);
   }
 
-  Panel minColour = new Panel();
-
-  Panel maxColour = new Panel();
-
-  Panel jPanel1 = new Panel();
-
-  Panel jPanel2 = new Panel();
-
-  Choice threshold = new Choice();
-
-  Panel jPanel3 = new Panel();
-
-  Panel jPanel4 = new Panel();
-
-  Scrollbar slider = new Scrollbar(Scrollbar.HORIZONTAL);
-
-  TextField thresholdValue = new TextField(20);
-
-  // BorderLayout borderLayout1 = new BorderLayout();
-
-  Checkbox thresholdIsMin = new Checkbox();
-
-  Checkbox colourFromLabel = new Checkbox();
-
-  private GraphLine threshline;
-
   @Override
   public void actionPerformed(ActionEvent evt)
   {
@@ -268,8 +275,13 @@ public class FeatureColourChooser extends Panel implements ActionListener,
       try
       {
         float f = new Float(thresholdValue.getText()).floatValue();
-        slider.setValue((int) (f * 1000));
+        slider.setValue((int) (f * SCALE_FACTOR_1K));
         adjustmentValueChanged(null);
+
+        /*
+         * force repaint of any Overview window or structure
+         */
+        changeColour(true);
       } catch (NumberFormatException ex)
       {
       }
@@ -284,7 +296,7 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     }
     else
     {
-      changeColour();
+      changeColour(true);
     }
   }
 
@@ -293,9 +305,13 @@ public class FeatureColourChooser extends Panel implements ActionListener,
   {
     maxColour.setEnabled(!colourFromLabel.getState());
     minColour.setEnabled(!colourFromLabel.getState());
-    changeColour();
+    changeColour(true);
   }
 
+  /**
+   * Handler called when the value of the threshold slider changes, either by
+   * user action or programmatically
+   */
   @Override
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
@@ -306,29 +322,32 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     }
   }
 
+  /**
+   * Responds to a change of colour threshold by computing the absolute value
+   * and refreshing the alignment.
+   */
   protected void valueChanged()
   {
     threshline.value = slider.getValue() / 1000f;
     cs.setThreshold(threshline.value);
-    changeColour();
+    changeColour(false);
     PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId());
-    // ap.paintAlignment(false);
   }
 
   public void minColour_actionPerformed(Color newCol)
   {
     if (newCol == null)
     {
-      UserDefinedColours udc = new UserDefinedColours(this,
+      new UserDefinedColours(this,
               minColour.getBackground(), owner,
-              MessageManager.getString("label.select_colour_minimum_value")); // frame.owner,
+              MessageManager.getString("label.select_colour_minimum_value"));
     }
     else
     {
       minColour.setBackground(newCol);
       minColour.setForeground(newCol);
       minColour.repaint();
-      changeColour();
+      changeColour(true);
     }
 
   }
@@ -337,10 +356,7 @@ public class FeatureColourChooser extends Panel implements ActionListener,
   {
     if (newCol == null)
     {
-
-      // UserDefinedColours udc = new UserDefinedColours(this,
-      // "Select Colour for Maximum Value",maxColour.getBackground(),true);
-      UserDefinedColours udc = new UserDefinedColours(this,
+      new UserDefinedColours(this,
               maxColour.getBackground(), owner,
               MessageManager.getString("label.select_colour_maximum_value"));
     }
@@ -349,11 +365,11 @@ public class FeatureColourChooser extends Panel implements ActionListener,
       maxColour.setBackground(newCol);
       maxColour.setForeground(newCol);
       maxColour.repaint();
-      changeColour();
+      changeColour(true);
     }
   }
 
-  void changeColour()
+  void changeColour(boolean updateOverview)
   {
     // Check if combobox is still adjusting
     if (adjusting)
@@ -361,14 +377,14 @@ public class FeatureColourChooser extends Panel implements ActionListener,
       return;
     }
 
-    int aboveThreshold = AnnotationColourGradient.NO_THRESHOLD;
+    int thresholdOption = AnnotationColourGradient.NO_THRESHOLD;
     if (threshold.getSelectedIndex() == 1)
     {
-      aboveThreshold = AnnotationColourGradient.ABOVE_THRESHOLD;
+      thresholdOption = AnnotationColourGradient.ABOVE_THRESHOLD;
     }
     else if (threshold.getSelectedIndex() == 2)
     {
-      aboveThreshold = AnnotationColourGradient.BELOW_THRESHOLD;
+      thresholdOption = AnnotationColourGradient.BELOW_THRESHOLD;
     }
 
     slider.setEnabled(true);
@@ -379,34 +395,34 @@ public class FeatureColourChooser extends Panel implements ActionListener,
     acg.setColourByLabel(colourFromLabel.getState());
     maxColour.setEnabled(!colourFromLabel.getState());
     minColour.setEnabled(!colourFromLabel.getState());
-    if (aboveThreshold == AnnotationColourGradient.NO_THRESHOLD)
+    if (thresholdOption == AnnotationColourGradient.NO_THRESHOLD)
     {
       slider.setEnabled(false);
       thresholdValue.setEnabled(false);
       thresholdValue.setText("");
     }
 
-    if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
+    if (thresholdOption != AnnotationColourGradient.NO_THRESHOLD)
     {
       adjusting = true;
       acg.setThreshold(threshline.value);
 
-      float range = max * 1000f - min * 1000f;
-
-      slider.setMinimum((int) (min * 1000));
-      slider.setMaximum((int) (max * 1000));
-      slider.setValue((int) (threshline.value * 1000));
+      slider.setMinimum((int) (min * SCALE_FACTOR_1K));
+      slider.setMaximum((int) (max * SCALE_FACTOR_1K));
+      slider.setValue((int) (threshline.value * SCALE_FACTOR_1K));
       thresholdValue.setText(threshline.value + "");
       slider.setEnabled(true);
       thresholdValue.setEnabled(true);
       adjusting = false;
     }
 
-    acg.setAboveThreshold(true);
+    acg.setAboveThreshold(thresholdOption == AnnotationColourGradient.ABOVE_THRESHOLD);
+    acg.setBelowThreshold(thresholdOption == AnnotationColourGradient.BELOW_THRESHOLD);
+
     if (thresholdIsMin.getState()
-            && aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
+            && thresholdOption != AnnotationColourGradient.NO_THRESHOLD)
     {
-      if (aboveThreshold == AnnotationColourGradient.ABOVE_THRESHOLD)
+      if (thresholdOption == AnnotationColourGradient.ABOVE_THRESHOLD)
       {
         acg = new FeatureColour(acg, threshline.value, max);
       }
@@ -418,16 +434,13 @@ public class FeatureColourChooser extends Panel implements ActionListener,
 
     fr.setColour(type, acg);
     cs = acg;
-    PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId());
-    // ap.paintAlignment(false);
+    fs.selectionChanged(updateOverview);
   }
 
   void reset()
   {
     fr.setColour(type, oldcs);
-    PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId());
-    // ap.paintAlignment(true);
-
+    fs.selectionChanged(true);
   }
 
   @Override
@@ -443,16 +456,19 @@ public class FeatureColourChooser extends Panel implements ActionListener,
   @Override
   public void mouseReleased(MouseEvent evt)
   {
-    if (evt.getSource() == minColour || evt.getSource() == maxColour)
+    if (evt.getSource() == minColour)
     {
-      // relay the event
-      actionPerformed(new ActionEvent(evt.getSource(), 1, "Clicked"));
+      minColour_actionPerformed(null);
+    }
+    else if (evt.getSource() == maxColour)
+    {
+      maxColour_actionPerformed(null);
     }
     else
     {
-      PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId());
+      changeColour(true);
+      // PaintRefresher.Refresh(this, fr.getViewport().getSequenceSetId());
     }
-    // ap.paintAlignment(true);
   }
 
   @Override
index 46498ad..f6f9727 100755 (executable)
@@ -545,7 +545,7 @@ public class FeatureSettings extends Panel implements ItemListener,
       Checkbox check = (Checkbox) featurePanel.getComponent(i);
       check.setState(!check.getState());
     }
-    selectionChanged();
+    selectionChanged(true);
   }
 
   private ItemListener groupItemListener = new ItemListener()
@@ -568,10 +568,10 @@ public class FeatureSettings extends Panel implements ItemListener,
   @Override
   public void itemStateChanged(ItemEvent evt)
   {
-    selectionChanged();
+    selectionChanged(true);
   }
 
-  void selectionChanged()
+  void selectionChanged(boolean updateOverview)
   {
     Component[] comps = featurePanel.getComponents();
     int cSize = comps.length;
@@ -592,7 +592,7 @@ public class FeatureSettings extends Panel implements ItemListener,
 
     fr.setFeaturePriority(data);
 
-    ap.paintAlignment(true);
+    ap.paintAlignment(updateOverview);
   }
 
   MyCheckbox selectedCheck;
index ccdfee1..456a38c 100755 (executable)
@@ -29,6 +29,7 @@ import jalview.viewmodel.ViewportListenerI;
 
 import java.awt.BorderLayout;
 import java.awt.CheckboxMenuItem;
+import java.awt.Cursor;
 import java.awt.Dimension;
 import java.awt.Panel;
 import java.awt.PopupMenu;
@@ -57,6 +58,8 @@ public class OverviewPanel extends Panel implements Runnable,
 
   private boolean updateRunning = false;
 
+  private boolean draggingBox = false;
+
   public OverviewPanel(AlignmentPanel alPanel)
   {
     this.av = alPanel.av;
@@ -118,28 +121,62 @@ public class OverviewPanel extends Panel implements Runnable,
   @Override
   public void mouseMoved(MouseEvent evt)
   {
+    if (od.isPositionInBox(evt.getX(), evt.getY()))
+    {
+      // display drag cursor at mouse position
+      setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+    }
+    else
+    {
+      // reset cursor
+      setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+    }
   }
 
   @Override
   public void mousePressed(MouseEvent evt)
   {
-    mouseAction(evt);
+    if ((evt.getModifiers()
+            & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
+    {
+      if (!Platform.isAMac())
+      {
+        showPopupMenu(evt);
+      }
+    }
+    else
+    {
+      if (!od.isPositionInBox(evt.getX(), evt.getY()))
+      {
+        // don't do anything if the mouse press is in the overview's box
+        // (wait to see if it's a drag instead)
+        // otherwise update the viewport
+        od.updateViewportFromMouse(evt.getX(), evt.getY(),
+                av.getAlignment().getHiddenSequences(),
+                av.getAlignment().getHiddenColumns());
+      }
+      else
+      {
+        draggingBox = true;
+        od.setDragPoint(evt.getX(), evt.getY(),
+                av.getAlignment().getHiddenSequences(),
+                av.getAlignment().getHiddenColumns());
+      }
+    }
   }
 
   @Override
   public void mouseReleased(MouseEvent evt)
   {
-    mouseAction(evt);
+    if (draggingBox)
+    {
+      draggingBox = false;
+    }
   }
 
   @Override
   public void mouseDragged(MouseEvent evt)
   {
-    mouseAction(evt);
-  }
-
-  private void mouseAction(MouseEvent evt)
-  {
     if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
     {
       if (!Platform.isAMac())
@@ -149,8 +186,20 @@ public class OverviewPanel extends Panel implements Runnable,
     }
     else
     {
-      od.updateViewportFromMouse(evt.getX(), evt.getY(), av.getAlignment()
-              .getHiddenSequences(), av.getAlignment().getHiddenColumns());
+      if (draggingBox)
+      {
+        // set the mouse position as a fixed point in the box
+        // and drag relative to that position
+        od.adjustViewportFromMouse(evt.getX(), evt.getY(),
+                av.getAlignment().getHiddenSequences(),
+                av.getAlignment().getHiddenColumns());
+      }
+      else
+      {
+        od.updateViewportFromMouse(evt.getX(), evt.getY(),
+                av.getAlignment().getHiddenSequences(),
+                av.getAlignment().getHiddenColumns());
+      }
       ap.paintAlignment(false);
     }
   }
index 7c6fad0..32507fe 100755 (executable)
@@ -27,6 +27,7 @@ import java.awt.Component;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
 import java.util.Vector;
 
 /**
@@ -37,7 +38,7 @@ import java.util.Vector;
  */
 public class PaintRefresher
 {
-  static Hashtable components;
+  static Map<String, Vector<Component>> components;
 
   /**
    * DOCUMENT ME!
@@ -51,12 +52,12 @@ public class PaintRefresher
   {
     if (components == null)
     {
-      components = new Hashtable();
+      components = new Hashtable<String, Vector<Component>>();
     }
 
     if (components.containsKey(seqSetId))
     {
-      Vector comps = (Vector) components.get(seqSetId);
+      Vector<Component> comps = components.get(seqSetId);
       if (!comps.contains(comp))
       {
         comps.addElement(comp);
@@ -64,7 +65,7 @@ public class PaintRefresher
     }
     else
     {
-      Vector vcoms = new Vector();
+      Vector<Component> vcoms = new Vector<>();
       vcoms.addElement(comp);
       components.put(seqSetId, vcoms);
     }
@@ -77,11 +78,9 @@ public class PaintRefresher
       return;
     }
 
-    Enumeration en = components.keys();
-    while (en.hasMoreElements())
+    for (String id : components.keySet())
     {
-      String id = en.nextElement().toString();
-      Vector comps = (Vector) components.get(id);
+      Vector<Component> comps = components.get(id);
       comps.removeElement(comp);
       if (comps.size() == 0)
       {
@@ -104,17 +103,17 @@ public class PaintRefresher
     }
 
     Component comp;
-    Vector comps = (Vector) components.get(id);
+    Vector<Component> comps = components.get(id);
 
     if (comps == null)
     {
       return;
     }
 
-    Enumeration e = comps.elements();
+    Enumeration<Component> e = comps.elements();
     while (e.hasMoreElements())
     {
-      comp = (Component) e.nextElement();
+      comp = e.nextElement();
 
       if (comp == source)
       {
@@ -240,8 +239,8 @@ public class PaintRefresher
 
   public static AlignmentPanel[] getAssociatedPanels(String id)
   {
-    Vector comps = (Vector) components.get(id);
-    Vector tmp = new Vector();
+    Vector<Component> comps = components.get(id);
+    Vector<Component> tmp = new Vector<>();
     int i, iSize = comps.size();
     for (i = 0; i < iSize; i++)
     {
index 98e9694..32443d8 100755 (executable)
@@ -312,7 +312,7 @@ public class HiddenSequences
       return startRow - visibleDistance;
     }
 
-    int index = startRow;
+    int index = Math.min(startRow, hiddenSequences.length - 1);
     int count = 0;
     while ((index > -1) && (count < visibleDistance))
     {
index a1c1bff..4172819 100644 (file)
@@ -49,29 +49,15 @@ import javax.swing.event.ChangeListener;
 public class FeatureColourChooser extends JalviewDialog
 {
   // FeatureSettings fs;
-  FeatureRenderer fr;
+  private FeatureRenderer fr;
 
   private FeatureColourI cs;
 
   private FeatureColourI oldcs;
 
-  /**
-   * 
-   * @return the last colour setting selected by user - either oldcs (which may
-   *         be a java.awt.Color) or the new GraduatedColor
-   */
-  public FeatureColourI getLastColour()
-  {
-    if (cs == null)
-    {
-      return oldcs;
-    }
-    return cs;
-  }
+  private AlignmentPanel ap;
 
-  AlignmentPanel ap;
-
-  boolean adjusting = false;
+  private boolean adjusting = false;
 
   final private float min;
 
@@ -79,25 +65,61 @@ public class FeatureColourChooser extends JalviewDialog
 
   final private float scaleFactor;
 
-  String type = null;
+  private String type = null;
+
+  private JPanel minColour = new JPanel();
+
+  private JPanel maxColour = new JPanel();
 
-  public FeatureColourChooser(FeatureRenderer frender, String type)
+  private JComboBox<String> threshold = new JComboBox<>();
+
+  private JSlider slider = new JSlider();
+
+  private JTextField thresholdValue = new JTextField(20);
+
+  // TODO implement GUI for tolower flag
+  // JCheckBox toLower = new JCheckBox();
+
+  private JCheckBox thresholdIsMin = new JCheckBox();
+
+  private JCheckBox colourByLabel = new JCheckBox();
+
+  private GraphLine threshline;
+
+  private Color oldmaxColour;
+
+  private Color oldminColour;
+
+  private ActionListener colourEditor = null;
+
+  /**
+   * Constructor
+   * 
+   * @param frender
+   * @param theType
+   */
+  public FeatureColourChooser(FeatureRenderer frender, String theType)
   {
-    this(frender, false, type);
+    this(frender, false, theType);
   }
 
-  public FeatureColourChooser(FeatureRenderer frender, boolean block,
-          String type)
+  /**
+   * Constructor, with option to make a blocking dialog (has to complete in the
+   * AWT event queue thread). Currently this option is always set to false.
+   * 
+   * @param frender
+   * @param blocking
+   * @param theType
+   */
+  FeatureColourChooser(FeatureRenderer frender, boolean blocking,
+          String theType)
   {
     this.fr = frender;
-    this.type = type;
+    this.type = theType;
     ap = fr.ap;
     String title = MessageManager.formatMessage(
-            "label.graduated_color_for_params", new String[] { type });
-    initDialogFrame(this, true, block, title, 480, 185);
-    // frame.setLayer(JLayeredPane.PALETTE_LAYER);
-    // Desktop.addInternalFrame(frame, "Graduated Feature Colour for "+type,
-    // 480, 145);
+            "label.graduated_color_for_params", new String[] { theType });
+    initDialogFrame(this, true, blocking, title, 480, 185);
 
     slider.addChangeListener(new ChangeListener()
     {
@@ -107,7 +129,7 @@ public class FeatureColourChooser extends JalviewDialog
         if (!adjusting)
         {
           thresholdValue.setText((slider.getValue() / scaleFactor) + "");
-          valueChanged();
+          sliderValueChanged();
         }
       }
     });
@@ -116,15 +138,18 @@ public class FeatureColourChooser extends JalviewDialog
       @Override
       public void mouseReleased(MouseEvent evt)
       {
+        /*
+         * only update Overview and/or structure colouring
+         * when threshold slider drag ends (mouse up)
+         */
         if (ap != null)
         {
           ap.paintAlignment(true);
         }
-        ;
       }
     });
 
-    float mm[] = fr.getMinMax().get(type)[0];
+    float mm[] = fr.getMinMax().get(theType)[0];
     min = mm[0];
     max = mm[1];
 
@@ -135,7 +160,7 @@ public class FeatureColourChooser extends JalviewDialog
      */
     scaleFactor = (max == min) ? 1f : 100f / (max - min);
 
-    oldcs = fr.getFeatureColours().get(type);
+    oldcs = fr.getFeatureColours().get(theType);
     if (!oldcs.isSimpleColour())
     {
       if (oldcs.isAutoScaled())
@@ -186,7 +211,7 @@ public class FeatureColourChooser extends JalviewDialog
 
     adjusting = false;
 
-    changeColour();
+    changeColour(false);
     waitForInput();
   }
 
@@ -224,20 +249,22 @@ public class FeatureColourChooser extends JalviewDialog
       }
     });
     maxColour.setBorder(new LineBorder(Color.black));
-    minText.setText(MessageManager.getString("label.min"));
+    JLabel minText = new JLabel(MessageManager.getString("label.min"));
     minText.setFont(JvSwingUtils.getLabelFont());
-    maxText.setText(MessageManager.getString("label.max"));
+    JLabel maxText = new JLabel(MessageManager.getString("label.max"));
     maxText.setFont(JvSwingUtils.getLabelFont());
-    this.setLayout(borderLayout1);
-    jPanel2.setLayout(flowLayout1);
+    this.setLayout(new BorderLayout());
+    JPanel jPanel1 = new JPanel();
     jPanel1.setBackground(Color.white);
+    JPanel jPanel2 = new JPanel();
+    jPanel2.setLayout(new FlowLayout());
     jPanel2.setBackground(Color.white);
     threshold.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        threshold_actionPerformed(e);
+        threshold_actionPerformed();
       }
     });
     threshold.setToolTipText(MessageManager
@@ -248,13 +275,15 @@ public class FeatureColourChooser extends JalviewDialog
             .getString("label.threshold_feature_above_threshold")); // index 1
     threshold.addItem(MessageManager
             .getString("label.threshold_feature_below_threshold")); // index 2
-    jPanel3.setLayout(flowLayout2);
+
+    JPanel jPanel3 = new JPanel();
+    jPanel3.setLayout(new FlowLayout());
     thresholdValue.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        thresholdValue_actionPerformed(e);
+        thresholdValue_actionPerformed();
       }
     });
     slider.setPaintLabels(false);
@@ -278,7 +307,7 @@ public class FeatureColourChooser extends JalviewDialog
       @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
-        thresholdIsMin_actionPerformed(actionEvent);
+        thresholdIsMin_actionPerformed();
       }
     });
     colourByLabel.setBackground(Color.white);
@@ -292,69 +321,34 @@ public class FeatureColourChooser extends JalviewDialog
       @Override
       public void actionPerformed(ActionEvent actionEvent)
       {
-        colourByLabel_actionPerformed(actionEvent);
+        colourByLabel_actionPerformed();
       }
     });
+
+    JPanel colourPanel = new JPanel();
     colourPanel.setBackground(Color.white);
     jPanel1.add(ok);
     jPanel1.add(cancel);
-    jPanel2.add(colourByLabel, java.awt.BorderLayout.WEST);
-    jPanel2.add(colourPanel, java.awt.BorderLayout.EAST);
+    jPanel2.add(colourByLabel, BorderLayout.WEST);
+    jPanel2.add(colourPanel, BorderLayout.EAST);
     colourPanel.add(minText);
     colourPanel.add(minColour);
     colourPanel.add(maxText);
     colourPanel.add(maxColour);
-    this.add(jPanel3, java.awt.BorderLayout.CENTER);
+    this.add(jPanel3, BorderLayout.CENTER);
     jPanel3.add(threshold);
     jPanel3.add(slider);
     jPanel3.add(thresholdValue);
     jPanel3.add(thresholdIsMin);
-    this.add(jPanel1, java.awt.BorderLayout.SOUTH);
-    this.add(jPanel2, java.awt.BorderLayout.NORTH);
+    this.add(jPanel1, BorderLayout.SOUTH);
+    this.add(jPanel2, BorderLayout.NORTH);
   }
 
-  JLabel minText = new JLabel();
-
-  JLabel maxText = new JLabel();
-
-  JPanel minColour = new JPanel();
-
-  JPanel maxColour = new JPanel();
-
-  JPanel colourPanel = new JPanel();
-
-  JPanel jPanel1 = new JPanel();
-
-  JPanel jPanel2 = new JPanel();
-
-  BorderLayout borderLayout1 = new BorderLayout();
-
-  JComboBox threshold = new JComboBox();
-
-  FlowLayout flowLayout1 = new FlowLayout();
-
-  JPanel jPanel3 = new JPanel();
-
-  FlowLayout flowLayout2 = new FlowLayout();
-
-  JSlider slider = new JSlider();
-
-  JTextField thresholdValue = new JTextField(20);
-
-  // TODO implement GUI for tolower flag
-  // JCheckBox toLower = new JCheckBox();
-
-  JCheckBox thresholdIsMin = new JCheckBox();
-
-  JCheckBox colourByLabel = new JCheckBox();
-
-  private GraphLine threshline;
-
-  private Color oldmaxColour;
-
-  private Color oldminColour;
-
-  public void minColour_actionPerformed()
+  /**
+   * Action on clicking the 'minimum colour' - open a colour chooser dialog, and
+   * set the selected colour (if the user does not cancel out of the dialog)
+   */
+  protected void minColour_actionPerformed()
   {
     Color col = JColorChooser.showDialog(this,
             MessageManager.getString("label.select_colour_minimum_value"),
@@ -365,10 +359,14 @@ public class FeatureColourChooser extends JalviewDialog
       minColour.setForeground(col);
     }
     minColour.repaint();
-    changeColour();
+    changeColour(true);
   }
 
-  public void maxColour_actionPerformed()
+  /**
+   * Action on clicking the 'maximum colour' - open a colour chooser dialog, and
+   * set the selected colour (if the user does not cancel out of the dialog)
+   */
+  protected void maxColour_actionPerformed()
   {
     Color col = JColorChooser.showDialog(this,
             MessageManager.getString("label.select_colour_maximum_value"),
@@ -379,10 +377,17 @@ public class FeatureColourChooser extends JalviewDialog
       maxColour.setForeground(col);
     }
     maxColour.repaint();
-    changeColour();
+    changeColour(true);
   }
 
-  void changeColour()
+  /**
+   * Constructs and sets the selected colour options as the colour for the
+   * feature type, and repaints the alignment, and optionally the Overview
+   * and/or structure viewer if open
+   * 
+   * @param updateOverview
+   */
+  void changeColour(boolean updateOverview)
   {
     // Check if combobox is still adjusting
     if (adjusting)
@@ -414,7 +419,6 @@ public class FeatureColourChooser extends JalviewDialog
     {
       acg = new FeatureColour(oldminColour = minColour.getBackground(),
               oldmaxColour = maxColour.getBackground(), min, max);
-
     }
 
     if (!hasThreshold)
@@ -426,7 +430,9 @@ public class FeatureColourChooser extends JalviewDialog
     }
     else if (threshline == null)
     {
-      // todo visual indication of feature threshold
+      /*
+       * todo not yet implemented: visual indication of feature threshold
+       */
       threshline = new GraphLine((max - min) / 2f, "Threshold", Color.black);
     }
 
@@ -439,7 +445,8 @@ public class FeatureColourChooser extends JalviewDialog
 
       slider.setMinimum((int) (min * scaleFactor));
       slider.setMaximum((int) (max * scaleFactor));
-      slider.setValue((int) (threshline.value * scaleFactor));
+      // slider.setValue((int) (threshline.value * scaleFactor));
+      slider.setValue(Math.round(threshline.value * scaleFactor));
       thresholdValue.setText(threshline.value + "");
       slider.setMajorTickSpacing((int) (range / 10f));
       slider.setEnabled(true);
@@ -488,7 +495,7 @@ public class FeatureColourChooser extends JalviewDialog
     }
     fr.setColour(type, acg);
     cs = acg;
-    ap.paintAlignment(false);
+    ap.paintAlignment(updateOverview);
   }
 
   @Override
@@ -503,7 +510,7 @@ public class FeatureColourChooser extends JalviewDialog
   @Override
   public void okPressed()
   {
-    changeColour();
+    changeColour(false);
   }
 
   @Override
@@ -512,61 +519,80 @@ public class FeatureColourChooser extends JalviewDialog
     reset();
   }
 
+  /**
+   * Action when the user cancels the dialog. All previous settings should be
+   * restored and rendered on the alignment, and any linked Overview window or
+   * structure.
+   */
   void reset()
   {
     fr.setColour(type, oldcs);
-    ap.paintAlignment(false);
+    ap.paintAlignment(true);
     cs = null;
   }
 
-  public void thresholdCheck_actionPerformed(ActionEvent e)
-  {
-    changeColour();
-  }
-
-  public void annotations_actionPerformed(ActionEvent e)
-  {
-    changeColour();
-  }
-
-  public void threshold_actionPerformed(ActionEvent e)
+  /**
+   * Action on change of choice of No / Above / Below Threshold
+   */
+  protected void threshold_actionPerformed()
   {
-    changeColour();
+    changeColour(true);
   }
 
-  public void thresholdValue_actionPerformed(ActionEvent e)
+  /**
+   * Action on text entry of a threshold value
+   */
+  protected void thresholdValue_actionPerformed()
   {
     try
     {
       float f = Float.parseFloat(thresholdValue.getText());
       slider.setValue((int) (f * scaleFactor));
       threshline.value = f;
+
+      /*
+       * force repaint of any Overview window or structure
+       */
+      ap.paintAlignment(true);
     } catch (NumberFormatException ex)
     {
     }
   }
 
-  public void valueChanged()
+  /**
+   * Action on change of threshold slider value. This may be done interactively
+   * (by moving the slider), or programmatically (to update the slider after
+   * manual input of a threshold value).
+   */
+  protected void sliderValueChanged()
   {
-    threshline.value = slider.getValue() / scaleFactor;
+    /*
+     * squash rounding errors by forcing min/max of slider to 
+     * actual min/max of feature score range
+     */
+    int value = slider.getValue();
+    threshline.value = value == slider.getMaximum() ? max
+            : (value == slider.getMinimum() ? min : value / scaleFactor);
     cs.setThreshold(threshline.value);
-    changeColour();
-    ap.paintAlignment(false);
+
+    /*
+     * repaint alignment, but not Overview or structure,
+     * to avoid overload while dragging the slider
+     */
+    changeColour(false);
   }
 
-  public void thresholdIsMin_actionPerformed(ActionEvent actionEvent)
+  protected void thresholdIsMin_actionPerformed()
   {
-    changeColour();
+    changeColour(true);
   }
 
-  public void colourByLabel_actionPerformed(ActionEvent actionEvent)
+  protected void colourByLabel_actionPerformed()
   {
-    changeColour();
+    changeColour(true);
   }
 
-  ActionListener colourEditor = null;
-
-  public void addActionListener(ActionListener graduatedColorEditor)
+  void addActionListener(ActionListener graduatedColorEditor)
   {
     if (colourEditor != null)
     {
@@ -576,4 +602,19 @@ public class FeatureColourChooser extends JalviewDialog
     colourEditor = graduatedColorEditor;
   }
 
+  /**
+   * Answers the last colour setting selected by user - either oldcs (which may
+   * be a java.awt.Color) or the new GraduatedColor
+   * 
+   * @return
+   */
+  FeatureColourI getLastColour()
+  {
+    if (cs == null)
+    {
+      return oldcs;
+    }
+    return cs;
+  }
+
 }
index 099d76a..1271fa3 100755 (executable)
@@ -154,7 +154,7 @@ public class IdPanel extends JPanel implements MouseListener,
       {
         av.getRanges().scrollRight(true);
       }
-      else
+      else if (!av.getWrapAlignment())
       {
         av.getRanges().scrollUp(false);
       }
@@ -165,7 +165,7 @@ public class IdPanel extends JPanel implements MouseListener,
       {
         av.getRanges().scrollRight(false);
       }
-      else
+      else if (!av.getWrapAlignment())
       {
         av.getRanges().scrollUp(true);
       }
index 7a4456e..c81ac2f 100755 (executable)
@@ -28,6 +28,7 @@ import jalview.viewmodel.OverviewDimensionsShowHidden;
 import jalview.viewmodel.ViewportListenerI;
 
 import java.awt.BorderLayout;
+import java.awt.Cursor;
 import java.awt.Dimension;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -65,6 +66,8 @@ public class OverviewPanel extends JPanel implements Runnable,
 
   private boolean showHidden = true;
 
+  private boolean draggingBox = false;
+
   /**
    * Creates a new OverviewPanel object.
    * 
@@ -109,9 +112,35 @@ public class OverviewPanel extends JPanel implements Runnable,
       {
         if (!SwingUtilities.isRightMouseButton(evt))
         {
-          od.updateViewportFromMouse(evt.getX(), evt.getY(), av
+          if (draggingBox)
+          {
+            // set the mouse position as a fixed point in the box
+            // and drag relative to that position
+            od.adjustViewportFromMouse(evt.getX(),
+                    evt.getY(), av.getAlignment().getHiddenSequences(),
+                    av.getAlignment().getHiddenColumns());
+          }
+          else
+          {
+            od.updateViewportFromMouse(evt.getX(), evt.getY(), av
                   .getAlignment().getHiddenSequences(), av.getAlignment()
                   .getHiddenColumns());
+          }
+        }
+      }
+
+      @Override
+      public void mouseMoved(MouseEvent evt)
+      {
+        if (od.isPositionInBox(evt.getX(), evt.getY()))
+        {
+          // display drag cursor at mouse position
+          setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+        }
+        else
+        {
+          // reset cursor
+          setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
         }
       }
     });
@@ -131,9 +160,31 @@ public class OverviewPanel extends JPanel implements Runnable,
         else
         // if (!av.getWrapAlignment())
         {
-          od.updateViewportFromMouse(evt.getX(), evt.getY(), av
-                  .getAlignment().getHiddenSequences(), av.getAlignment()
-                  .getHiddenColumns());
+          if (!od.isPositionInBox(evt.getX(), evt.getY()))
+          {
+            // don't do anything if the mouse press is in the overview's box
+            // (wait to see if it's a drag instead)
+            // otherwise update the viewport
+            od.updateViewportFromMouse(evt.getX(), evt.getY(),
+                    av.getAlignment().getHiddenSequences(),
+                    av.getAlignment().getHiddenColumns());
+          }
+          else
+          {
+            draggingBox = true;
+            od.setDragPoint(evt.getX(), evt.getY(),
+                    av.getAlignment().getHiddenSequences(),
+                    av.getAlignment().getHiddenColumns());
+          }
+        }
+      }
+
+      @Override
+      public void mouseReleased(MouseEvent evt)
+      {
+        if (draggingBox)
+        {
+          draggingBox = false;
         }
       }
 
index 7ac07ac..f54c1eb 100644 (file)
@@ -192,6 +192,44 @@ public abstract class OverviewDimensions
           HiddenSequences hiddenSeqs, HiddenColumns hiddenCols);
 
   /**
+   * Update the viewport location from a mouse drag within the overview's box
+   * 
+   * @param mousex
+   *          x location of mouse
+   * @param mousey
+   *          y location of mouse
+   * @param hiddenSeqs
+   *          the alignment's hidden sequences
+   * @param hiddenCols
+   *          the alignment's hidden columns
+   */
+  public abstract void adjustViewportFromMouse(int mousex, int mousey,
+          HiddenSequences hiddenSeqs, HiddenColumns hiddenCols);
+
+  /**
+   * Initialise dragging from the mouse - must be called on initial mouse click
+   * before using adjustViewportFromMouse in drag operations
+   * 
+   * @param mousex
+   *          x location of mouse
+   * @param mousey
+   *          y location of mouse
+   * @param hiddenSeqs
+   *          the alignment's hidden sequences
+   * @param hiddenCols
+   *          the alignment's hidden columns
+   */
+  public abstract void setDragPoint(int x, int y,
+          HiddenSequences hiddenSeqs, HiddenColumns hiddenCols);
+
+  /*
+   * Move the viewport so that the top left corner of the overview's box 
+   * is at the mouse position (leftx, topy)
+   */
+  protected abstract void updateViewportFromTopLeft(int leftx, int topy,
+          HiddenSequences hiddenSeqs, HiddenColumns hiddenCols);
+
+  /**
    * Set the overview panel's box position to match the viewport
    * 
    * @param hiddenSeqs
@@ -225,6 +263,9 @@ public abstract class OverviewDimensions
    */
   protected abstract void resetAlignmentDims();
 
+  /*
+   * Given the box coordinates in residues and sequences, set the box dimensions in the overview window
+   */
   protected void setBoxPosition(int startRes, int startSeq, int vpwidth,
           int vpheight)
   {
@@ -241,4 +282,31 @@ public abstract class OverviewDimensions
     // boxHeight is the height in sequences translated to pixels
     boxHeight = Math.round((float) vpheight * sequencesHeight / alheight);
   }
+
+  /**
+   * Answers if a mouse position is in the overview's red box
+   * 
+   * @param x
+   *          mouse x position
+   * @param y
+   *          mouse y position
+   * @return true if (x,y) is inside the box
+   */
+  public boolean isPositionInBox(int x, int y)
+  {
+    return (x > boxX && y > boxY && x < boxX + boxWidth
+            && y < boxY + boxHeight);
+  }
+
+  /*
+   * Given the centre x position, calculate the box's left x position
+   */
+  protected abstract int getLeftXFromCentreX(int mousex, HiddenColumns hidden);
+
+  /*
+   * Given the centre y position, calculate the box's top y position
+   */
+  protected abstract int getTopYFromCentreY(int mousey,
+          HiddenSequences hidden);
+
 }
\ No newline at end of file
index 4d64f1c..c78b8a3 100644 (file)
@@ -12,6 +12,12 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions
 {
   private ViewportRanges ranges;
 
+  private int xdiff; // when dragging, difference in alignment units between
+                     // start residue and original mouse click position
+
+  private int ydiff; // when dragging, difference in alignment units between
+                     // start sequence and original mouse click position
+
   public OverviewDimensionsHideHidden(ViewportRanges vpranges,
           boolean showAnnotationPanel)
   {
@@ -24,37 +30,51 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions
   public void updateViewportFromMouse(int mousex, int mousey,
           HiddenSequences hiddenSeqs, HiddenColumns hiddenCols)
   {
-    resetAlignmentDims();
+    int xAsRes = getLeftXFromCentreX(mousex, hiddenCols);
+    int yAsSeq = getTopYFromCentreY(mousey, hiddenSeqs);
+
+    updateViewportFromTopLeft(xAsRes, yAsSeq, hiddenSeqs, hiddenCols);
+
+  }
+
+  @Override
+  public void adjustViewportFromMouse(int mousex, int mousey,
+          HiddenSequences hiddenSeqs, HiddenColumns hiddenCols)
+  {
+    // calculate translation in pixel terms:
+    // get mouse location in viewport coords, add translation in viewport
+    // coords, and update viewport as usual
+    int vpx = Math.round((float) mousex * alwidth / width);
+    int vpy = Math.round((float) mousey * alheight / sequencesHeight);
 
-    int x = mousex;
-    int y = mousey;
+    updateViewportFromTopLeft(vpx + xdiff, vpy + ydiff, hiddenSeqs,
+            hiddenCols);
 
-    if (x < 0)
+  }
+
+  @Override
+  protected void updateViewportFromTopLeft(int leftx, int topy,
+          HiddenSequences hiddenSeqs, HiddenColumns hiddenCols)
+  {
+    int xAsRes = leftx;
+    int yAsSeq = topy;
+    resetAlignmentDims();
+
+    if (xAsRes < 0)
     {
-      x = 0;
+      xAsRes = 0;
     }
 
-    if (y < 0)
+    if (yAsSeq < 0)
     {
-      y = 0;
+      yAsSeq = 0;
     }
 
     if (ranges.isWrappedMode())
     {
-      y = 0; // sorry, no vertical scroll when wrapped
+      yAsSeq = 0; // sorry, no vertical scroll when wrapped
     }
 
-    //
-    // Convert x value to residue position
-    //
-
-    // need to determine where scrollCol should be, given x
-    // to do this also need to know width of viewport, and some hidden column
-    // correction
-
-    // convert x to residues - this is an absolute position
-    int xAsRes = Math.round((float) x * alwidth / width);
-
     // get viewport width in residues
     int vpwidth = ranges.getViewportWidth();
 
@@ -73,13 +93,7 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions
       }
     }
 
-
-    //
-    // Convert y value to sequence position
-    //
-
-    // convert y to residues
-    int yAsSeq = Math.round((float) y * alheight / sequencesHeight);
+    // Determine where scrollRow should be, given visYAsSeq
 
     // get viewport height in sequences
     // add 1 because height includes both endSeq and startSeq
@@ -101,7 +115,6 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions
     // update viewport
     ranges.setStartRes(xAsRes);
     ranges.setStartSeq(yAsSeq);
-
   }
 
   @Override
@@ -132,4 +145,32 @@ public class OverviewDimensionsHideHidden extends OverviewDimensions
     alwidth = ranges.getVisibleAlignmentWidth();
     alheight = ranges.getVisibleAlignmentHeight();
   }
+
+  @Override
+  protected int getLeftXFromCentreX(int mousex, HiddenColumns hidden)
+  {
+    int vpx = Math.round((float) mousex * alwidth / width);
+    return vpx - ranges.getViewportWidth() / 2;
+  }
+
+  @Override
+  protected int getTopYFromCentreY(int mousey, HiddenSequences hidden)
+  {
+    int vpy = Math.round((float) mousey * alheight / sequencesHeight);
+    return vpy - ranges.getViewportHeight() / 2;
+  }
+
+  @Override
+  public void setDragPoint(int x, int y, HiddenSequences hiddenSeqs,
+          HiddenColumns hiddenCols)
+  {
+    // get alignment position of x and box (can get directly from vpranges) and
+    // calculate difference between the positions
+    int vpx = Math.round((float) x * alwidth / width);
+    int vpy = Math.round((float) y * alheight / sequencesHeight);
+
+    xdiff = ranges.getStartRes() - vpx;
+    ydiff = ranges.getStartSeq() - vpy;
+  }
+
 }
index 62e8000..5bd4bba 100644 (file)
@@ -32,6 +32,12 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
 {
   private ViewportRanges ranges;
 
+  private int xdiff; // when dragging, difference in alignment units between
+                     // start residue and original mouse click position
+
+  private int ydiff; // when dragging, difference in alignment units between
+                     // start sequence and original mouse click position
+
   /**
    * Create an OverviewDimensions object
    * 
@@ -66,46 +72,66 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
   public void updateViewportFromMouse(int mousex, int mousey,
           HiddenSequences hiddenSeqs, HiddenColumns hiddenCols)
   {
-    int x = mousex;
-    int y = mousey;
+    // convert mousex and mousey to alignment units as well as
+    // translating to top left corner of viewport - this is an absolute position
+    int xAsRes = getLeftXFromCentreX(mousex, hiddenCols);
+    int yAsSeq = getTopYFromCentreY(mousey, hiddenSeqs);
 
-    resetAlignmentDims();
+    // convert to visible positions
+    int visXAsRes = hiddenCols.findColumnPosition(xAsRes);
+    yAsSeq = hiddenSeqs.adjustForHiddenSeqs(
+            hiddenSeqs.findIndexWithoutHiddenSeqs(yAsSeq));
+    yAsSeq = Math.max(yAsSeq, 0); // -1 if before first visible sequence
+    int visYAsSeq = hiddenSeqs.findIndexWithoutHiddenSeqs(yAsSeq);
+    visYAsSeq = Math.max(visYAsSeq, 0); // -1 if before first visible sequence
 
-    if (x < 0)
-    {
-      x = 0;
-    }
+    // update viewport accordingly
+    updateViewportFromTopLeft(visXAsRes, visYAsSeq, hiddenSeqs, hiddenCols);
+  }
+
+  @Override
+  public void adjustViewportFromMouse(int mousex, int mousey,
+          HiddenSequences hiddenSeqs, HiddenColumns hiddenCols)
+  {
+    // calculate translation in pixel terms:
+    // get mouse location in viewport coords, add translation in viewport
+    // coords,
+    // convert back to pixel coords
+    int vpx = Math.round((float) mousex * alwidth / width);
+    int visXAsRes = hiddenCols.findColumnPosition(vpx) + xdiff;
+
+    int vpy = Math.round((float) mousey * alheight / sequencesHeight);
+    int visYAsRes = hiddenSeqs.findIndexWithoutHiddenSeqs(vpy) + ydiff;
+
+    // update viewport accordingly
+    updateViewportFromTopLeft(visXAsRes, visYAsRes,
+            hiddenSeqs,
+            hiddenCols);
+  }
+
+  @Override
+  protected void updateViewportFromTopLeft(int leftx, int topy,
+          HiddenSequences hiddenSeqs, HiddenColumns hiddenCols)
+  {
+    int visXAsRes = leftx;
+    int visYAsSeq = topy;
+    resetAlignmentDims();
 
-    if (y < 0)
+    if (visXAsRes < 0)
     {
-      y = 0;
+      visXAsRes = 0;
     }
 
-    if (ranges.isWrappedMode())
+    if (visYAsSeq < 0)
     {
-      y = 0; // sorry, no vertical scroll when wrapped
+      visYAsSeq = 0;
     }
 
-    //
-    // Convert x value to residue position
-    //
-
-    // need to determine where scrollCol should be, given x
-    // to do this also need to know width of viewport, and some hidden column
-    // correction
-
-    // convert x to residues - this is an absolute position
-    int xAsRes = Math.round((float) x * alwidth / width);
+    // Determine where scrollCol should be, given visXAsRes
 
     // get viewport width in residues
     int vpwidth = ranges.getViewportWidth();
 
-    // get where x should be when accounting for hidden cols
-    // if x is in a hidden col region, shift to left - but we still need
-    // absolute position
-    // so convert back after getting visible region position
-    int visXAsRes = hiddenCols.findColumnPosition(xAsRes);
-
     // check in case we went off the edge of the alignment
     int visAlignWidth = hiddenCols.findColumnPosition(alwidth - 1);
     if (visXAsRes + vpwidth - 1 > visAlignWidth)
@@ -124,28 +150,14 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
       }
     }
 
-    //
-    // Convert y value to sequence position
-    //
-
-    // convert y to residues
-    int yAsSeq = Math.round((float) y * alheight / sequencesHeight);
+    // Determine where scrollRow should be, given visYAsSeq
 
     // get viewport height in sequences
     int vpheight = ranges.getViewportHeight();
 
-    // get where y should be when accounting for hidden rows
-    // if y is in a hidden row region, shift up - but we still need absolute
-    // position,
-    // so convert back after getting visible region position
-    yAsSeq = hiddenSeqs.adjustForHiddenSeqs(hiddenSeqs
-            .findIndexWithoutHiddenSeqs(yAsSeq));
-    yAsSeq = Math.max(yAsSeq, 0); // -1 if before first visible sequence
-
     // check in case we went off the edge of the alignment
     int visAlignHeight = hiddenSeqs.findIndexWithoutHiddenSeqs(alheight);
-    int visYAsSeq = hiddenSeqs.findIndexWithoutHiddenSeqs(yAsSeq);
-    visYAsSeq = Math.max(visYAsSeq, 0); // -1 if before first visible sequence
+
     if (visYAsSeq + vpheight - 1 > visAlignHeight)
     {
       // went past the end of the alignment, adjust backwards
@@ -213,4 +225,34 @@ public class OverviewDimensionsShowHidden extends OverviewDimensions
     alwidth = ranges.getAbsoluteAlignmentWidth();
     alheight = ranges.getAbsoluteAlignmentHeight();
   }
+
+  @Override
+  protected int getLeftXFromCentreX(int mousex, HiddenColumns hidden)
+  {
+    int vpx = Math.round((float) mousex * alwidth / width);
+    return hidden.subtractVisibleColumns(ranges.getViewportWidth() / 2,
+            vpx);
+  }
+
+  @Override
+  protected int getTopYFromCentreY(int mousey, HiddenSequences hidden)
+  {
+    int vpy = Math.round((float) mousey * alheight / sequencesHeight);
+    return hidden.subtractVisibleRows(ranges.getViewportHeight() / 2, vpy);
+  }
+
+  @Override
+  public void setDragPoint(int x, int y, HiddenSequences hiddenSeqs,
+          HiddenColumns hiddenCols)
+  {
+    // get alignment position of x and box (can get directly from vpranges) and
+    // calculate difference between the positions
+    int vpx = Math.round((float) x * alwidth / width);
+    int vpy = Math.round((float) y * alheight / sequencesHeight);
+
+    xdiff = ranges.getStartRes() - hiddenCols.findColumnPosition(vpx);
+    ydiff = ranges.getStartSeq()
+            - hiddenSeqs.findIndexWithoutHiddenSeqs(vpy);
+  }
+
 }
index 45340d9..3b688db 100644 (file)
@@ -79,9 +79,13 @@ public class FeaturesFileTest
      * updated - JAL-1904), and verify (some) feature group colours
      */
     colours = af.getFeatureRenderer().getFeatureColours();
-    assertEquals("26 feature group colours not found", 26, colours.size());
+    assertEquals("27 feature group colours not found", 27, colours.size());
     assertEquals(colours.get("Cath").getColour(), new Color(0x93b1d1));
     assertEquals(colours.get("ASX-MOTIF").getColour(), new Color(0x6addbb));
+    FeatureColourI kdColour = colours.get("kdHydrophobicity");
+    assertTrue(kdColour.isGraduatedColour());
+    assertTrue(kdColour.isAboveThreshold());
+    assertEquals(-2f, kdColour.getThreshold());
 
     /*
      * verify (some) features on sequences
index 0e931eb..f6a9d32 100644 (file)
@@ -21,6 +21,8 @@
 package jalview.viewmodel;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
 
 import jalview.analysis.AlignmentGenerator;
 import jalview.datamodel.Alignment;
@@ -56,7 +58,7 @@ public class OverviewDimensionsHideHiddenTest
 
   ViewportRanges vpranges;
 
-  Hashtable<SequenceI, SequenceCollectionI> hiddenRepSequences = new Hashtable<SequenceI, SequenceCollectionI>();
+  Hashtable<SequenceI, SequenceCollectionI> hiddenRepSequences = new Hashtable<>();
 
   HiddenColumns hiddenCols = new HiddenColumns();
 
@@ -210,7 +212,7 @@ public class OverviewDimensionsHideHiddenTest
     assertEquals(od.getBoxX(), 0);
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
-    assertEquals(vpranges.getStartSeq(),
+    assertEquals(vpranges.getStartSeq() + vpranges.getViewportHeight() / 2,
             Math.round((float) 10 * alheight / od.getSequencesHeight()));
     assertEquals(vpranges.getStartRes(), 0);
 
@@ -219,14 +221,13 @@ public class OverviewDimensionsHideHiddenTest
     assertEquals(od.getBoxY(), 0);
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
-    assertEquals(vpranges.getStartRes(),
-            Math.round((float) 6 * alwidth / od.getWidth()));
+    assertEquals(vpranges.getStartRes(), 0);
     assertEquals(vpranges.getStartSeq(), 0);
 
     // overly large boxX value reset to width-boxWidth
-    mouseClick(od, 100, 6);
+    mouseClick(od, 101, 6);
     assertEquals(od.getBoxX(), od.getWidth() - od.getBoxWidth());
-    assertEquals(od.getBoxY(), 6);
+    assertEquals(od.getBoxY(), 1);
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
     assertEquals(vpranges.getStartRes(),
@@ -238,7 +239,7 @@ public class OverviewDimensionsHideHiddenTest
 
     // overly large boxY value reset to sequenceHeight - boxHeight
     mouseClick(od, 10, 520);
-    assertEquals(od.getBoxX(), 10);
+    assertEquals(od.getBoxX(), 0);
     assertEquals(od.getBoxY(), od.getSequencesHeight() - od.getBoxHeight());
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
@@ -266,18 +267,19 @@ public class OverviewDimensionsHideHiddenTest
                     / od.getSequencesHeight()));
 
     // move viewport so startRes non-zero and then mouseclick
-    moveViewportH(50);
+    moveViewportH(20);
 
     // click at viewport position
     int oldboxx = od.getBoxX();
     int oldboxy = od.getBoxY();
-    mouseClick(od, od.getBoxX() + 5, od.getBoxY() + 2);
-    assertEquals(od.getBoxX(), oldboxx + 5);
+    mouseClick(od, od.getBoxX() + od.getBoxWidth() / 2 + 6,
+            od.getBoxY() + od.getBoxHeight() / 2 + 3);
+    assertEquals(od.getBoxX(), oldboxx + 6);
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
     assertEquals(vpranges.getStartRes(),
             Math.round((float) od.getBoxX() * alwidth / od.getWidth()));
-    assertEquals(od.getBoxY(), oldboxy + 2);
+    assertEquals(od.getBoxY(), oldboxy + 3);
     assertEquals(
             vpranges.getStartSeq(),
             Math.round((float) od.getBoxY() * alheight
@@ -311,20 +313,20 @@ public class OverviewDimensionsHideHiddenTest
     int lastHiddenCol = 30;
     hiddenCols.hideColumns(0, lastHiddenCol);
 
-    testBoxIsAtClickPoint(0, 0);
+    testBoxIsAtClickPoint(boxWidth / 2, boxHeight / 2);
 
     // click to right of hidden columns, box moves to click point
-    testBoxIsAtClickPoint(40, 0);
+    testBoxIsAtClickPoint(41 + boxWidth / 2, boxHeight / 2);
     assertEquals(vpranges.getStartSeq(), 0);
     assertEquals(vpranges.getStartRes(),
-            Math.round((float) 40 * alwidth / od.getWidth()));
+            Math.round((float) 41 * alwidth / od.getWidth()));
 
     // click to right of hidden columns such that box runs over right hand side
     // of alignment
     // box position is adjusted away from the edge
     // overly large boxX value reset to width-boxWidth
-    int xpos = 100;
-    mouseClick(od, xpos, 0);
+    int xpos = 100 + boxWidth / 2;
+    mouseClick(od, xpos, boxHeight / 2);
     assertEquals(od.getBoxX(), Math.round(od.getWidth()) - boxWidth);
     assertEquals(od.getBoxY(), 0);
     assertEquals(od.getBoxWidth(), boxWidth);
@@ -342,7 +344,7 @@ public class OverviewDimensionsHideHiddenTest
   public void testFromMouseWithHiddenColsInMiddle()
   {
     od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols);
-    testBoxIsAtClickPoint(0, 0);
+    testBoxIsAtClickPoint(boxWidth / 2, boxHeight / 2);
     assertEquals(od.getBoxX(), 0);
     assertEquals(od.getBoxY(), 0);
     assertEquals(od.getBoxWidth(), boxWidth);
@@ -355,7 +357,7 @@ public class OverviewDimensionsHideHiddenTest
     hiddenCols.hideColumns(firstHidden, lastHidden);
 
     od.setBoxPosition(al.getHiddenSequences(), hiddenCols);
-    testBoxIsAtClickPoint(0, 0);
+    testBoxIsAtClickPoint(boxWidth / 2, boxHeight / 2);
     assertEquals(od.getBoxX(), 0);
     assertEquals(od.getBoxY(), 0);
     assertEquals(od.getBoxWidth(), boxWidth);
@@ -364,45 +366,51 @@ public class OverviewDimensionsHideHiddenTest
 
     // move box so that it overlaps with hidden cols on one side
     // box width, boxX and scrollCol as for unhidden case
-    int xpos = 55 - boxWidth; // 55 is position in overview approx halfway
+    int xpos = 54 - boxWidth / 2; // 54 is position in overview approx halfway
                               // between cols 60 and 70
-    mouseClick(od, xpos, 0);
-    testBoxIsAtClickPoint(xpos, 0);
-    assertEquals(vpranges.getStartRes(),
-            Math.round(xpos * alwidth / od.getWidth()));
+    mouseClick(od, xpos, boxHeight / 2);
+    testBoxIsAtClickPoint(xpos, boxHeight / 2);
+    assertEquals(vpranges.getStartRes(), 1 + // rounding
+            Math.round((xpos - boxWidth / 2) * alwidth / od.getWidth()));
     assertEquals(vpranges.getStartSeq(), 0);
 
     // move box so that it completely covers hidden cols
     // box width, boxX and scrollCol as for unhidden case
     xpos = 33;
-    mouseClick(od, xpos, 0);
-    testBoxIsAtClickPoint(xpos, 0);
+    mouseClick(od, xpos, boxHeight / 2);
+    testBoxIsAtClickPoint(xpos, boxHeight / 2);
     assertEquals(vpranges.getStartRes(),
-            Math.round((float) xpos * alwidth / od.getWidth()));
+            Math.round((float) (xpos - boxWidth / 2) * alwidth
+                    / od.getWidth()));
     assertEquals(vpranges.getStartSeq(), 0);
 
     // move box so boxX is in hidden cols, box overhangs at right
     // boxX and scrollCol at left of hidden area, box width unchanged
-    xpos = 50;
-    mouseClick(od, xpos, 0);
-    testBoxIsAtClickPoint(xpos, 0);
-    assertEquals(vpranges.getStartRes(),
-            Math.round((float) xpos * alwidth / od.getWidth()));
+    xpos = Math.round((float) 50 * od.getWidth() / alwidth) + boxWidth / 2;
+    mouseClick(od, xpos, boxHeight / 2);
+    assertEquals(od.getBoxX() + od.getBoxWidth() / 2, xpos);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
+    assertEquals(vpranges.getStartRes(), 50);
     assertEquals(vpranges.getStartSeq(), 0);
 
     // move box so boxX is to right of hidden cols, but does not go beyond full
     // width of alignment
     // box width, boxX and scrollCol all as for non-hidden case
-    xpos = 75;
-    testBoxIsAtClickPoint(xpos, 0);
+    xpos = Math.round((float) 75 * od.getWidth() / alwidth) + boxWidth / 2;
+    mouseClick(od, xpos, boxHeight / 2);
+    assertEquals(od.getBoxX() + od.getBoxWidth() / 2, xpos);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
     assertEquals(vpranges.getStartSeq(), 0);
-    assertEquals(vpranges.getStartRes(),
-            Math.round(xpos * alwidth / od.getWidth()));
+    assertEquals(vpranges.getStartRes(), 75);
     
     // move box so it goes beyond full width of alignment
     // boxX, scrollCol adjusted back, box width normal
     xpos = 3000;
-    mouseClick(od, xpos, 0);
+    mouseClick(od, xpos, boxHeight / 2);
     assertEquals(od.getBoxX(), Math.round(od.getWidth()) - boxWidth);
     assertEquals(od.getBoxY(), 0);
     assertEquals(od.getBoxWidth(), boxWidth);
@@ -440,19 +448,22 @@ public class OverviewDimensionsHideHiddenTest
 
     // click to left of hidden cols, without overlapping
     // boxX, scrollCol and width as normal
-    int xpos = 5;
-    testBoxIsAtClickPoint(xpos, 0);
-    assertEquals(vpranges.getStartSeq(), 0);
-    assertEquals(vpranges.getStartRes(),
-            Math.round((float) xpos * alwidth / od.getWidth()));
+    int xpos = 30;
+    int ypos = 6;
+    testBoxIsAtClickPoint(xpos, ypos);
+    assertEquals(vpranges.getStartSeq(), Math.round(
+            (float) (ypos - boxHeight / 2) * alheight / od.getHeight()));
+    assertEquals(vpranges.getStartRes(), Math.round(
+            (float) (xpos - boxWidth / 2) * alwidth / od.getWidth()));
 
     // click to left of hidden cols, with overlap
     // boxX and scrollCol adjusted for hidden cols, width normal
-    xpos = Math.round((float) 145 * od.getWidth() / alwidth) - boxWidth;
-    mouseClick(od, xpos, 0);
-    testBoxIsAtClickPoint(xpos, 0);
+    xpos = Math.round((float) 144 * od.getWidth() / alwidth) - boxWidth;
+    mouseClick(od, xpos, boxHeight / 2);
+    testBoxIsAtClickPoint(xpos, boxHeight / 2);
     assertEquals(vpranges.getStartRes(),
-            Math.round((float) xpos * alwidth / od.getWidth()));
+            Math.round((float) (xpos - boxWidth / 2) * alwidth
+                    / od.getWidth()));
     assertEquals(vpranges.getStartSeq(), 0);
 
     // click off end of alignment
@@ -777,9 +788,9 @@ public class OverviewDimensionsHideHiddenTest
     assertEquals(od.getBoxHeight(), boxHeight);
 
     // click below hidden rows
-    mouseClick(od, 0, 150);
+    mouseClick(od, 0, 151 + boxHeight / 2);
     assertEquals(od.getBoxX(), 0);
-    assertEquals(od.getBoxY(), 150);
+    assertEquals(od.getBoxY(), 151);
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
   }
@@ -825,22 +836,22 @@ public class OverviewDimensionsHideHiddenTest
     assertEquals(od.getBoxHeight(), boxHeight);
 
     // click above hidden rows, so that box overlaps
-    int ypos = 35; // column value in residues
+    int ypos = 35 + viewHeight / 2; // row value in residues
     mouseClick(od, 0,
             Math.round((float) ypos * od.getSequencesHeight() / alheight));
     assertEquals(od.getBoxX(), 0);
     assertEquals(od.getBoxY(),
-            Math.round((float) ypos * od.getSequencesHeight() / alheight));
+            Math.round((float) 35 * od.getSequencesHeight() / alheight));
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
 
     // click so that box straddles hidden rows
-    ypos = 44; // column value in residues
+    ypos = 45 + viewHeight / 2; // row value in residues
     mouseClick(od, 0,
             Math.round((float) ypos * od.getSequencesHeight() / alheight));
     assertEquals(od.getBoxX(), 0);
     assertEquals(od.getBoxY(),
-            Math.round((float) ypos * od.getSequencesHeight() / alheight));
+            Math.round((float) 45 * od.getSequencesHeight() / alheight));
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
   }
@@ -884,18 +895,18 @@ public class OverviewDimensionsHideHiddenTest
     assertEquals(od.getBoxHeight(), boxHeight);
 
     // click above hidden rows
-    int ypos = 40; // row 40
+    int ypos = 41 + viewHeight / 2; // row 41
     mouseClick(od, 0,
             Math.round((float) ypos * od.getSequencesHeight() / alheight));
     assertEquals(od.getBoxX(), 0);
     assertEquals(od.getBoxY(),
-            Math.round((float) ypos * od.getSequencesHeight() / alheight));
+            Math.round((float) 41 * od.getSequencesHeight() / alheight));
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
 
     // click above hidden rows so box overlaps
     // boxY, boxHeight remains same
-    ypos = 497; // row 497
+    ypos = 497 + viewHeight / 2; // row 497
     mouseClick(od, 0,
             Math.round((float) ypos * od.getSequencesHeight() / alheight));
     assertEquals(od.getBoxX(), 0);
@@ -908,6 +919,86 @@ public class OverviewDimensionsHideHiddenTest
     assertEquals(od.getBoxHeight(), boxHeight);
   }
 
+  /**
+   * Test the function to determine if a point is in the overview's box or not
+   */
+  @Test(groups = { "Functional" })
+  public void testPositionInBox()
+  {
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols);
+
+    assertFalse(od.isPositionInBox(0, 0));
+    assertTrue(od.isPositionInBox(10, 9));
+    assertFalse(od.isPositionInBox(0, 9));
+    assertFalse(od.isPositionInBox(9, 0));
+    assertFalse(od.isPositionInBox(75, 20));
+
+    // hide columns in the box area
+    // makes absolutely no difference
+    hiddenCols.hideColumns(1, 4);
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols);
+    assertFalse(od.isPositionInBox(0, 0));
+    assertTrue(od.isPositionInBox(10, 9));
+    assertFalse(od.isPositionInBox(0, 9));
+    assertFalse(od.isPositionInBox(9, 0));
+    assertFalse(od.isPositionInBox(75, 20));
+
+    // hide sequences in box area
+    // makes absolutely no difference
+    hideSequences(1, 3);
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols);
+    assertFalse(od.isPositionInBox(0, 0));
+    assertTrue(od.isPositionInBox(10, 9));
+    assertFalse(od.isPositionInBox(0, 9));
+    assertFalse(od.isPositionInBox(9, 0));
+    assertFalse(od.isPositionInBox(75, 20));
+  }
+
+  /**
+   * Test the dragging functionality
+   */
+  @Test(groups = { "Functional" })
+  public void testDragging()
+  {
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols);
+    od.setDragPoint(4, 16, al.getHiddenSequences(),
+            hiddenCols);
+    od.adjustViewportFromMouse(20, 22,
+            al.getHiddenSequences(), hiddenCols);
+
+    // updates require an OverviewPanel to exist which it doesn't here
+    // so call setBoxPosition() as it would be called by the AlignmentPanel
+    // normally
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols);
+
+    // corner moves 16 (20-4) right and 6 (22-16) up
+    assertEquals(od.getBoxX(), 16);
+    assertEquals(od.getBoxY(), 6);
+
+    // hide columns - makes no difference
+    hiddenCols.hideColumns(1, 4);
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols);
+    od.setDragPoint(4, 16, al.getHiddenSequences(), hiddenCols);
+    od.adjustViewportFromMouse(20, 22, al.getHiddenSequences(), hiddenCols);
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols);
+
+    // corner moves 16 (20-4) right and 6 (22-16) up
+    assertEquals(od.getBoxX(), 16);
+    assertEquals(od.getBoxY(), 6);
+
+    // hide sequences in box area
+    // makes absolutely no difference
+    hideSequences(1, 3);
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols);
+    od.setDragPoint(4, 16, al.getHiddenSequences(), hiddenCols);
+    od.adjustViewportFromMouse(20, 22, al.getHiddenSequences(), hiddenCols);
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols);
+
+    // corner moves 16 (20-4) right and 6 (22-16) up
+    assertEquals(od.getBoxX(), 16);
+    assertEquals(od.getBoxY(), 6);
+  }
+
   /*
    * Move viewport horizontally: startRes + previous width gives new horizontal extent. Vertical extent stays the same.
    */
@@ -956,8 +1047,8 @@ public class OverviewDimensionsHideHiddenTest
   private void testBoxIsAtClickPoint(int xpos, int ypos)
   {
     mouseClick(od, xpos, ypos);
-    assertEquals(od.getBoxX(), xpos);
-    assertEquals(od.getBoxY(), ypos);
+    assertEquals(od.getBoxX() + od.getBoxWidth() / 2, xpos);
+    assertEquals(od.getBoxY() + od.getBoxHeight() / 2, ypos);
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
 
index 66480c9..842a0fd 100644 (file)
@@ -21,6 +21,8 @@
 package jalview.viewmodel;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
 
 import jalview.analysis.AlignmentGenerator;
 import jalview.datamodel.Alignment;
@@ -55,7 +57,7 @@ public class OverviewDimensionsShowHiddenTest
 
   ViewportRanges vpranges;
 
-  Hashtable<SequenceI, SequenceCollectionI> hiddenRepSequences = new Hashtable<SequenceI, SequenceCollectionI>();
+  Hashtable<SequenceI, SequenceCollectionI> hiddenRepSequences = new Hashtable<>();
 
   HiddenColumns hiddenCols = new HiddenColumns();
 
@@ -209,7 +211,7 @@ public class OverviewDimensionsShowHiddenTest
     assertEquals(od.getBoxX(), 0);
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
-    assertEquals(vpranges.getStartSeq(),
+    assertEquals(vpranges.getStartSeq() + vpranges.getViewportHeight() / 2,
             Math.round((float) 10 * alheight / od.getSequencesHeight()));
     assertEquals(vpranges.getStartRes(), 0);
 
@@ -218,14 +220,13 @@ public class OverviewDimensionsShowHiddenTest
     assertEquals(od.getBoxY(), 0);
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
-    assertEquals(vpranges.getStartRes(),
-            Math.round((float) 6 * alwidth / od.getWidth()));
+    assertEquals(vpranges.getStartRes(), 0);
     assertEquals(vpranges.getStartSeq(), 0);
 
     // overly large boxX value reset to width-boxWidth
-    mouseClick(od, 100, 6);
+    mouseClick(od, 101, 6);
     assertEquals(od.getBoxX(), od.getWidth() - od.getBoxWidth());
-    assertEquals(od.getBoxY(), 6);
+    assertEquals(od.getBoxY(), 1);
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
     assertEquals(vpranges.getStartRes(),
@@ -237,11 +238,11 @@ public class OverviewDimensionsShowHiddenTest
 
     // overly large boxY value reset to sequenceHeight - boxHeight
     mouseClick(od, 10, 520);
-    assertEquals(od.getBoxX(), 10);
+    assertEquals(od.getBoxX(), 0);
     assertEquals(od.getBoxY(), od.getSequencesHeight() - od.getBoxHeight());
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
-    assertEquals(vpranges.getStartRes(),
+    assertEquals(0,
             Math.round((float) od.getBoxX() * alwidth / od.getWidth()));
 
     // here (float) od.getBoxY() * alheight / od.getSequencesHeight() = 507.5
@@ -265,18 +266,19 @@ public class OverviewDimensionsShowHiddenTest
                     / od.getSequencesHeight()));
 
     // move viewport so startRes non-zero and then mouseclick
-    moveViewportH(50);
+    moveViewportH(20);
 
     // click at viewport position
     int oldboxx = od.getBoxX();
     int oldboxy = od.getBoxY();
-    mouseClick(od, od.getBoxX() + 5, od.getBoxY() + 2);
-    assertEquals(od.getBoxX(), oldboxx + 5);
+    mouseClick(od, od.getBoxX() + od.getBoxWidth() / 2 + 6,
+            od.getBoxY() + od.getBoxHeight() / 2 + 3);
+    assertEquals(od.getBoxX(), oldboxx + 6);
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
     assertEquals(vpranges.getStartRes(),
             Math.round((float) od.getBoxX() * alwidth / od.getWidth()));
-    assertEquals(od.getBoxY(), oldboxy + 2);
+    assertEquals(od.getBoxY(), oldboxy + 3);
     assertEquals(
             vpranges.getStartSeq(),
             Math.round((float) od.getBoxY() * alheight
@@ -332,10 +334,15 @@ public class OverviewDimensionsShowHiddenTest
     assertEquals(vpranges.getStartRes(), 0);
 
     // click to right of hidden columns, box moves to click point
-    testBoxIsAtClickPoint(40, 0);
+    mouseClick(od, 60 + boxWidth / 2, boxHeight / 2);
+    assertEquals(od.getBoxX(), 60);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
     assertEquals(vpranges.getStartSeq(), 0);
     assertEquals(vpranges.getStartRes(),
-            Math.round((float) 40 * alwidth / od.getWidth())
+            Math.round(
+                    (float) 60 * alwidth / od.getWidth())
                     - (lastHiddenCol + 1));
 
     // click to right of hidden columns such that box runs over right hand side
@@ -343,7 +350,7 @@ public class OverviewDimensionsShowHiddenTest
     // box position is adjusted away from the edge
     // overly large boxX value reset to width-boxWidth
     xpos = 100;
-    mouseClick(od, xpos, 5);
+    mouseClick(od, xpos + boxWidth / 2, 5 + boxHeight / 2);
     assertEquals(od.getBoxX(), od.getWidth() - od.getBoxWidth());
     assertEquals(od.getBoxY(), 5);
     assertEquals(od.getBoxWidth(), boxWidth);
@@ -385,10 +392,10 @@ public class OverviewDimensionsShowHiddenTest
 
     // move box so that it overlaps with hidden cols on one side
     // box width changes, boxX and scrollCol as for unhidden case
-    int xpos = 55 - boxWidth; // 55 is position in overview approx halfway
+    int xpos = 54 - boxWidth / 2; // 54 is position in overview approx halfway
                               // between cols 60 and 70
-    mouseClick(od, xpos, 0);
-    assertEquals(od.getBoxX(), xpos);
+    mouseClick(od, xpos, boxHeight / 2);
+    assertEquals(od.getBoxX(), xpos - boxWidth / 2);
     assertEquals(od.getBoxY(), 0);
     assertEquals(
             od.getBoxWidth(),
@@ -396,14 +403,15 @@ public class OverviewDimensionsShowHiddenTest
                     * od.getWidth() / alwidth));
     assertEquals(od.getBoxHeight(), boxHeight);
     assertEquals(vpranges.getStartRes(),
-            Math.round(xpos * alwidth / od.getWidth()));
+            Math.round((xpos - boxWidth / 2) * alwidth / od.getWidth())
+                    + 1); // +1 for rounding
     assertEquals(vpranges.getStartSeq(), 0);
 
     // move box so that it completely covers hidden cols
     // box width changes, boxX and scrollCol as for hidden case
-    xpos = 33;
+    xpos = 24 + boxWidth / 2;
     mouseClick(od, xpos, 0);
-    assertEquals(od.getBoxX(), xpos);
+    assertEquals(od.getBoxX(), 24);
     assertEquals(od.getBoxY(), 0);
     assertEquals(
             od.getBoxWidth(),
@@ -411,42 +419,28 @@ public class OverviewDimensionsShowHiddenTest
                     * od.getWidth() / alwidth));
     assertEquals(od.getBoxHeight(), boxHeight);
     assertEquals(vpranges.getStartRes(),
-            Math.round((float) xpos * alwidth / od.getWidth()));
-    assertEquals(vpranges.getStartSeq(), 0);
-
-    // move box so boxX is in hidden cols, box overhangs at right
-    // boxX and scrollCol at left of hidden area, box width extends across
-    // hidden region
-    xpos = 50;
-    mouseClick(od, xpos, 0);
-    assertEquals(od.getBoxX(),
-            Math.round((float) (firstHidden - 1) * od.getWidth() / alwidth));
-    assertEquals(od.getBoxY(), 0);
-    assertEquals(
-            od.getBoxWidth(),
-            boxWidth
-                    + Math.round((float) (lastHidden - firstHidden + 1)
-                            * od.getWidth() / alwidth));
-    assertEquals(od.getBoxHeight(), boxHeight);
-    assertEquals(vpranges.getStartRes(), firstHidden - 1);
+            Math.round((float) 24 * alwidth / od.getWidth()));
     assertEquals(vpranges.getStartSeq(), 0);
 
     // move box so boxX is to right of hidden cols, but does not go beyond full
     // width of alignment
     // box width, boxX and scrollCol all as for non-hidden case
-    xpos = 75;
-    testBoxIsAtClickPoint(xpos, 0);
+    xpos = Math.round((float) 75 * od.getWidth() / alwidth) + boxWidth / 2;
+    mouseClick(od, xpos, boxHeight / 2);
+    assertEquals(od.getBoxX(), xpos - boxWidth / 2);
+    assertEquals(od.getBoxY(), 0);
+    assertEquals(od.getBoxWidth(), boxWidth);
+    assertEquals(od.getBoxHeight(), boxHeight);
     assertEquals(vpranges.getStartSeq(), 0);
     assertEquals(vpranges.getStartRes(),
-            Math.round(xpos * alwidth / od.getWidth())
-                    - (lastHidden - firstHidden + 1));
+            75 - (lastHidden - firstHidden + 1));
     
     // move box so it goes beyond full width of alignment
     // boxX, scrollCol adjusted back, box width normal
     xpos = 3000;
     mouseClick(od, xpos, 5);
     assertEquals(od.getBoxX(), od.getWidth() - od.getBoxWidth());
-    assertEquals(od.getBoxY(), 5);
+    assertEquals(od.getBoxY(), 0);
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
     assertEquals(
@@ -487,24 +481,29 @@ public class OverviewDimensionsShowHiddenTest
 
     // click to left of hidden cols, without overlapping
     // boxX, scrollCol and width as normal
-    int xpos = 5;
-    testBoxIsAtClickPoint(xpos, 0);
-    assertEquals(vpranges.getStartSeq(), 0);
-    assertEquals(vpranges.getStartRes(),
-            Math.round((float) xpos * alwidth / od.getWidth()));
+    int xpos = 30;
+    int ypos = 6;
+    testBoxIsAtClickPoint(xpos, ypos);
+    assertEquals(vpranges.getStartSeq(), Math
+            .round((float) (ypos - boxHeight / 2) * alheight
+                    / od.getHeight()));
+    assertEquals(vpranges.getStartRes(), Math.round(
+            (float) (xpos - boxWidth / 2) * alwidth / od.getWidth()));
 
     // click to left of hidden cols, with overlap
     // boxX and scrollCol adjusted for hidden cols, width normal
     xpos = Math.round((float) 145 * od.getWidth() / alwidth) - boxWidth;
-    mouseClick(od, xpos, 0);
+    mouseClick(od, xpos + boxWidth / 2, boxHeight / 2);
     assertEquals(od.getBoxX(),
-            Math.round((float) (firstHidden - 1) * od.getWidth() / alwidth)
+            Math.round((float) (firstHidden - 1)
+                    * od.getWidth() / alwidth)
                     - boxWidth + 1);
     assertEquals(od.getBoxY(), 0);
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
     assertEquals(vpranges.getStartRes(),
-            Math.round((float) od.getBoxX() * alwidth / od.getWidth()));
+            Math.round((float) (od.getBoxX()) * alwidth
+                    / od.getWidth()));
     assertEquals(vpranges.getStartSeq(), 0);
 
     // click in hidden cols
@@ -823,7 +822,7 @@ public class OverviewDimensionsShowHiddenTest
     assertEquals(od.getBoxHeight(), boxHeight);
 
     // click below hidden rows
-    mouseClick(od, 0, 150);
+    mouseClick(od, 0, 150 + boxHeight/2);
     assertEquals(od.getBoxX(), 0);
     assertEquals(od.getBoxY(), 150);
     assertEquals(od.getBoxWidth(), boxWidth);
@@ -860,26 +859,15 @@ public class OverviewDimensionsShowHiddenTest
     assertEquals(od.getBoxHeight(), boxHeight);
 
     // click above hidden rows, so that box overlaps
-    int ypos = 35; // column value in residues
-    mouseClick(od, 0,
-            Math.round((float) ypos * od.getSequencesHeight() / alheight));
-    assertEquals(od.getBoxX(), 0);
-    assertEquals(od.getBoxY(),
-            Math.round((float) ypos * od.getSequencesHeight() / alheight));
-    assertEquals(od.getBoxWidth(), boxWidth);
-    assertEquals(
-            od.getBoxHeight(),
-            boxHeight
-                    + Math.round((float) (lastHiddenRow - firstHiddenRow + 1)
-                            * od.getSequencesHeight() / alheight));
-
-    // click so that box straddles hidden rows
-    ypos = 44; // column value in residues
+    int rowpos = 35; // row value in residues
+    int centrepos = 43; // centre row
     mouseClick(od, 0,
-            Math.round((float) ypos * od.getSequencesHeight() / alheight));
+            Math.round((float) centrepos * od.getSequencesHeight()
+                    / alheight));
     assertEquals(od.getBoxX(), 0);
     assertEquals(od.getBoxY(),
-            Math.round((float) ypos * od.getSequencesHeight() / alheight));
+            Math.round(
+                    (float) rowpos * od.getSequencesHeight() / alheight));
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(
             od.getBoxHeight(),
@@ -915,19 +903,19 @@ public class OverviewDimensionsShowHiddenTest
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
 
-    // click above hidden rows
-    int ypos = 40; // row 40
+    // click above hidden rows, no overlap
+    int ypos = 40 + viewHeight / 2; // top is row 40
     mouseClick(od, 0,
             Math.round((float) ypos * od.getSequencesHeight() / alheight));
     assertEquals(od.getBoxX(), 0);
     assertEquals(od.getBoxY(),
-            Math.round((float) ypos * od.getSequencesHeight() / alheight));
+            Math.round((float) 40 * od.getSequencesHeight() / alheight));
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);
 
     // click above hidden rows so box overlaps
     // boxY moved upwards, boxHeight remains same
-    ypos = 497; // row 497
+    ypos = 497 + viewHeight / 2; // row 497
     mouseClick(od, 0,
             Math.round((float) ypos * od.getSequencesHeight() / alheight));
     assertEquals(od.getBoxX(), 0);
@@ -939,7 +927,7 @@ public class OverviewDimensionsShowHiddenTest
     assertEquals(od.getBoxHeight(), boxHeight);
 
     // click within hidden rows
-    ypos = 505;
+    ypos = 505 + boxHeight / 2;
     mouseClick(od, 0,
             Math.round((float) ypos * od.getSequencesHeight() / alheight));
     assertEquals(od.getBoxX(), 0);
@@ -951,6 +939,105 @@ public class OverviewDimensionsShowHiddenTest
     assertEquals(od.getBoxHeight(), boxHeight);
   }
 
+  /**
+   * Test the function to determine if a point is in the overview's box or not
+   */
+  @Test(groups = { "Functional" })
+  public void testPositionInBox()
+  {
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols);
+
+    assertFalse(od.isPositionInBox(0, 0));
+    assertTrue(od.isPositionInBox(10, 9));
+    assertFalse(od.isPositionInBox(0, 9));
+    assertFalse(od.isPositionInBox(9, 0));
+    assertFalse(od.isPositionInBox(75, 20));
+
+    assertTrue(od.isPositionInBox(47, 6));
+    assertFalse(od.isPositionInBox(48, 6));
+    assertTrue(od.isPositionInBox(47, 9));
+    assertFalse(od.isPositionInBox(47, 10));
+
+    // hide columns in the box area
+    // extends area where a point is considered to be in the box
+    hiddenCols.hideColumns(1, 4);
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols);
+    assertFalse(od.isPositionInBox(0, 0));
+    assertTrue(od.isPositionInBox(10, 9));
+    assertFalse(od.isPositionInBox(0, 9));
+    assertFalse(od.isPositionInBox(9, 0));
+    assertFalse(od.isPositionInBox(75, 20));
+
+    assertTrue(od.isPositionInBox(47, 6));
+    assertTrue(od.isPositionInBox(48, 6));
+    assertTrue(od.isPositionInBox(47, 9));
+    assertFalse(od.isPositionInBox(47, 10));
+
+    // hide sequences in box area
+    // extends area where a point is considered to be in the box
+    hideSequences(1, 3);
+    ColumnSelection cs = new ColumnSelection();
+    hiddenCols.revealAllHiddenColumns(cs);
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols);
+    assertFalse(od.isPositionInBox(0, 0));
+    assertTrue(od.isPositionInBox(10, 9));
+    assertFalse(od.isPositionInBox(0, 9));
+    assertFalse(od.isPositionInBox(9, 0));
+    assertFalse(od.isPositionInBox(75, 20));
+
+    assertTrue(od.isPositionInBox(47, 6));
+    assertFalse(od.isPositionInBox(48, 6));
+    assertTrue(od.isPositionInBox(47, 9));
+    assertTrue(od.isPositionInBox(47, 10));
+  }
+
+  /**
+   * Test the dragging functionality
+   */
+  @Test(groups = { "Functional" })
+  public void testDragging()
+  {
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols);
+    od.setDragPoint(4, 16, al.getHiddenSequences(), hiddenCols);
+    od.adjustViewportFromMouse(20, 22, al.getHiddenSequences(), hiddenCols);
+
+    // updates require an OverviewPanel to exist which it doesn't here
+    // so call setBoxPosition() as it would be called by the AlignmentPanel
+    // normally
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols);
+
+    // corner moves 16 (20-4) right and 6 (22-16) up
+    assertEquals(od.getBoxX(), 16);
+    assertEquals(od.getBoxY(), 6);
+
+    // hide columns - box moves drag distance + hidden cols, vertically makes no
+    // difference
+    hiddenCols.hideColumns(1, 4);
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols);
+    od.setDragPoint(4, 16, al.getHiddenSequences(), hiddenCols);
+    od.adjustViewportFromMouse(20, 22, al.getHiddenSequences(), hiddenCols);
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols);
+
+    // corner moves 16 (20-4) + hiddenCols right and 6 (22-16) down
+    assertEquals(od.getBoxX(),
+            16 + Math.round((float) 4 * od.getWidth() / alwidth));
+    assertEquals(od.getBoxY(), 6);
+
+    // hide sequences in box area
+    // makes absolutely no difference
+    hideSequences(1, 3);
+    od.updateViewportFromMouse(0, 0, al.getHiddenSequences(), hiddenCols);
+    od.setDragPoint(4, 16, al.getHiddenSequences(), hiddenCols);
+    od.adjustViewportFromMouse(20, 22, al.getHiddenSequences(), hiddenCols);
+    od.setBoxPosition(al.getHiddenSequences(), hiddenCols);
+
+    // corner moves 16 (20-4) + hiddenCols right and 6 (22-16) + hiddenRows down
+    assertEquals(od.getBoxX(),
+            16 + Math.round((float) 4 * od.getWidth() / alwidth));
+    assertEquals(od.getBoxY(),
+            6 + Math.round((float) 3 * od.getHeight() / alheight));
+  }
+
   /*
    * Move viewport horizontally: startRes + previous width gives new horizontal extent. Vertical extent stays the same.
    */
@@ -993,14 +1080,14 @@ public class OverviewDimensionsShowHiddenTest
   }
   
   /*
-   * Test that the box is positioned with the top left corner at xpos, ypos
+   * Test that the box is positioned with the centre at xpos, ypos
    * and with the original width and height
    */
   private void testBoxIsAtClickPoint(int xpos, int ypos)
   {
     mouseClick(od, xpos, ypos);
-    assertEquals(od.getBoxX(), xpos);
-    assertEquals(od.getBoxY(), ypos);
+    assertEquals(od.getBoxX() + od.getBoxWidth() / 2, xpos);
+    assertEquals(od.getBoxY() + od.getBoxHeight() / 2, ypos);
     assertEquals(od.getBoxWidth(), boxWidth);
     assertEquals(od.getBoxHeight(), boxHeight);