JAL-2504 use (and update) defaults for new feature only if not supplied
[jalview.git] / src / jalview / appletgui / SeqPanel.java
index e488a32..d46cc34 100644 (file)
  */
 package jalview.appletgui;
 
+import jalview.api.AlignViewportI;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.SearchResultMatchI;
 import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.schemes.ResidueProperties;
+import jalview.structure.SelectionListener;
 import jalview.structure.SelectionSource;
 import jalview.structure.SequenceListener;
 import jalview.structure.StructureSelectionManager;
+import jalview.structure.VamsasSource;
+import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.ViewportRanges;
 
 import java.awt.BorderLayout;
 import java.awt.Font;
@@ -46,7 +56,7 @@ import java.awt.event.MouseMotionListener;
 import java.util.Vector;
 
 public class SeqPanel extends Panel implements MouseMotionListener,
-        MouseListener, SequenceListener
+        MouseListener, SequenceListener, SelectionListener
 {
 
   public SeqCanvas seqCanvas;
@@ -109,6 +119,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     seqCanvas.addMouseListener(this);
     ssm = StructureSelectionManager.getStructureSelectionManager(av.applet);
     ssm.addStructureViewerListener(this);
+    ssm.addSelectionListener(this);
 
     seqCanvas.repaint();
   }
@@ -159,8 +170,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
   void setCursorPosition()
   {
-    SequenceI sequence = av.getAlignment().getSequenceAt(
-            seqCanvas.cursorY);
+    SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
 
     seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1;
     scrollToVisible();
@@ -171,19 +181,22 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     seqCanvas.cursorX += dx;
     seqCanvas.cursorY += dy;
     if (av.hasHiddenColumns()
-            && !av.getColumnSelection().isVisible(seqCanvas.cursorX))
+            && !av.getAlignment().getHiddenColumns()
+                    .isVisible(seqCanvas.cursorX))
     {
       int original = seqCanvas.cursorX - dx;
       int maxWidth = av.getAlignment().getWidth();
 
-      while (!av.getColumnSelection().isVisible(seqCanvas.cursorX)
+      while (!av.getAlignment().getHiddenColumns()
+              .isVisible(seqCanvas.cursorX)
               && seqCanvas.cursorX < maxWidth && seqCanvas.cursorX > 0)
       {
         seqCanvas.cursorX += dx;
       }
 
       if (seqCanvas.cursorX >= maxWidth
-              || !av.getColumnSelection().isVisible(seqCanvas.cursorX))
+              || !av.getAlignment().getHiddenColumns()
+                      .isVisible(seqCanvas.cursorX))
       {
         seqCanvas.cursorX = original;
       }
@@ -212,22 +225,24 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     }
 
     endEditing();
-    if (av.wrapAlignment)
+    if (av.getWrapAlignment())
     {
       ap.scrollToWrappedVisible(seqCanvas.cursorX);
     }
     else
     {
-      while (seqCanvas.cursorY < av.startSeq)
+      ViewportRanges ranges = av.getRanges();
+      HiddenColumns hidden = av.getAlignment().getHiddenColumns();
+      while (seqCanvas.cursorY < ranges.getStartSeq())
       {
         ap.scrollUp(true);
       }
-      while (seqCanvas.cursorY + 1 > av.endSeq)
+      while (seqCanvas.cursorY > ranges.getEndSeq())
       {
         ap.scrollUp(false);
       }
-      while (seqCanvas.cursorX < av.getColumnSelection()
-              .adjustForHiddenColumns(av.startRes))
+      while (seqCanvas.cursorX < hidden.adjustForHiddenColumns(ranges
+              .getStartRes()))
       {
 
         if (!ap.scrollRight(false))
@@ -235,8 +250,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
           break;
         }
       }
-      while (seqCanvas.cursorX > av.getColumnSelection()
-              .adjustForHiddenColumns(av.endRes))
+      while (seqCanvas.cursorX > hidden.adjustForHiddenColumns(ranges
+              .getEndRes()))
       {
         if (!ap.scrollRight(true))
         {
@@ -252,8 +267,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
   void setSelectionAreaAtCursor(boolean topLeft)
   {
-    SequenceI sequence = av.getAlignment().getSequenceAt(
-            seqCanvas.cursorY);
+    SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
 
     if (av.getSelectionGroup() != null)
     {
@@ -393,43 +407,97 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     return 1;
   }
 
+  /**
+   * Set status message in alignment panel
+   * 
+   * @param sequence
+   *          aligned sequence object
+   * @param res
+   *          alignment column
+   * @param seq
+   *          index of sequence in alignment
+   * @return position of res in sequence
+   */
   void setStatusMessage(SequenceI sequence, int res, int seq)
   {
-    StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: "
-            + sequence.getName());
-
-    Object obj = null;
+    // TODO remove duplication of identical gui method
+    StringBuilder text = new StringBuilder(32);
+    String seqno = seq == -1 ? "" : " " + (seq + 1);
+    text.append("Sequence" + seqno + " ID: " + sequence.getName());
+
+    String residue = null;
+    /*
+     * Try to translate the display character to residue name (null for gap).
+     */
+    final String displayChar = String.valueOf(sequence.getCharAt(res));
     if (av.getAlignment().isNucleotide())
     {
-      obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)
-              + "");
-      if (obj != null)
+      residue = ResidueProperties.nucleotideName.get(displayChar);
+      if (residue != null)
       {
-        text.append(" Nucleotide: ");
+        text.append(" Nucleotide: ").append(residue);
       }
     }
     else
     {
-      obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
-      if (obj != null)
+      residue = "X".equalsIgnoreCase(displayChar) ? "X" : ("*"
+              .equals(displayChar) ? "STOP" : ResidueProperties.aa2Triplet
+              .get(displayChar));
+      if (residue != null)
       {
-        text.append("  Residue: ");
+        text.append(" Residue: ").append(residue);
       }
     }
 
-    if (obj != null)
+    int pos = -1;
+    if (residue != null)
     {
-
-      if (obj != "")
-      {
-        text.append(obj + " (" + sequence.findPosition(res) + ")");
-      }
+      pos = sequence.findPosition(res);
+      text.append(" (").append(Integer.toString(pos)).append(")");
     }
 
     ap.alignFrame.statusBar.setText(text.toString());
+  }
+
+  /**
+   * Set the status bar message to highlight the first matched position in
+   * search results.
+   * 
+   * @param results
+   * @return true if results were matched, false if not
+   */
+  private boolean setStatusMessage(SearchResultsI results)
+  {
+    AlignmentI al = this.av.getAlignment();
+    int sequenceIndex = al.findIndex(results);
+    if (sequenceIndex == -1)
+    {
+      return false;
+    }
+    SequenceI ds = al.getSequenceAt(sequenceIndex).getDatasetSequence();
+    for (SearchResultMatchI m : results.getResults())
+    {
+      SequenceI seq = m.getSequence();
+      if (seq.getDatasetSequence() != null)
+      {
+        seq = seq.getDatasetSequence();
+      }
 
+      if (seq == ds)
+      {
+        /*
+         * Convert position in sequence (base 1) to sequence character array
+         * index (base 0)
+         */
+        int start = m.getStart() - m.getSequence().getStart();
+        setStatusMessage(seq, start, sequenceIndex);
+        return true;
+      }
+    }
+    return false;
   }
 
+  @Override
   public void mousePressed(MouseEvent evt)
   {
     lastMousePress = evt.getPoint();
@@ -480,6 +548,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     return;
   }
 
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
     SequenceI sequence = av.getAlignment().getSequenceAt(findSeq(evt));
@@ -498,21 +567,22 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
       if (features != null && features.length > 0)
       {
-        SearchResults highlight = new SearchResults();
+        SearchResultsI highlight = new SearchResults();
         highlight.addResult(sequence, features[0].getBegin(),
                 features[0].getEnd());
         seqCanvas.highlightSearchResults(highlight);
       }
       if (features != null && features.length > 0)
       {
-        seqCanvas.getFeatureRenderer().amendFeatures(new SequenceI[]
-        { sequence }, features, false, ap);
+        seqCanvas.getFeatureRenderer().amendFeatures(
+                new SequenceI[] { sequence }, features, false, ap);
 
         seqCanvas.highlightSearchResults(null);
       }
     }
   }
 
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
     mouseDragging = false;
@@ -538,17 +608,17 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     int res = 0;
     int x = evt.getX();
 
-    if (av.wrapAlignment)
+    if (av.getWrapAlignment())
     {
 
-      int hgap = av.charHeight;
-      if (av.scaleAboveWrapped)
+      int hgap = av.getCharHeight();
+      if (av.getScaleAboveWrapped())
       {
-        hgap += av.charHeight;
+        hgap += av.getCharHeight();
       }
 
-      int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
-              + seqCanvas.getAnnotationHeight();
+      int cHeight = av.getAlignment().getHeight() * av.getCharHeight()
+              + hgap + seqCanvas.getAnnotationHeight();
 
       int y = evt.getY();
       y -= hgap;
@@ -561,19 +631,20 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       }
 
       wrappedBlock = y / cHeight;
-      wrappedBlock += av.getStartRes() / cwidth;
+      wrappedBlock += av.getRanges().getStartRes() / cwidth;
 
       res = wrappedBlock * cwidth + x / av.getCharWidth();
 
     }
     else
     {
-      res = (x / av.getCharWidth()) + av.getStartRes();
+      res = (x / av.getCharWidth()) + av.getRanges().getStartRes();
     }
 
     if (av.hasHiddenColumns())
     {
-      res = av.getColumnSelection().adjustForHiddenColumns(res);
+      res = av.getAlignment().getHiddenColumns()
+              .adjustForHiddenColumns(res);
     }
 
     return res;
@@ -596,16 +667,16 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     int seq = 0;
     int y = evt.getY();
 
-    if (av.wrapAlignment)
+    if (av.getWrapAlignment())
     {
-      int hgap = av.charHeight;
-      if (av.scaleAboveWrapped)
+      int hgap = av.getCharHeight();
+      if (av.getScaleAboveWrapped())
       {
-        hgap += av.charHeight;
+        hgap += av.getCharHeight();
       }
 
-      int cHeight = av.getAlignment().getHeight() * av.charHeight + hgap
-              + seqCanvas.getAnnotationHeight();
+      int cHeight = av.getAlignment().getHeight() * av.getCharHeight()
+              + hgap + seqCanvas.getAnnotationHeight();
 
       y -= hgap;
 
@@ -618,7 +689,9 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     }
     else
     {
-      seq = Math.min((y / av.getCharHeight()) + av.getStartSeq(), av
+      seq = Math.min((y / av.getCharHeight())
+              + av.getRanges().getStartSeq(),
+              av
               .getAlignment().getHeight() - 1);
       if (seq < 0)
       {
@@ -656,6 +729,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
   String lastMessage;
 
+  @Override
   public void mouseOverSequence(SequenceI sequence, int index, int pos)
   {
     String tmp = sequence.hashCode() + index + "";
@@ -667,25 +741,35 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     lastMessage = tmp;
   }
 
-  public void highlightSequence(SearchResults results)
+  @Override
+  public void highlightSequence(SearchResultsI results)
   {
-    if (av.followHighlight)
+    if (av.isFollowHighlight())
     {
       if (ap.scrollToPosition(results, true))
       {
         ap.alignFrame.repaint();
       }
     }
+    setStatusMessage(results);
     seqCanvas.highlightSearchResults(results);
 
   }
 
+  @Override
+  public VamsasSource getVamsasSource()
+  {
+    return this.ap == null ? null : this.ap.av;
+  }
+
+  @Override
   public void updateColours(SequenceI seq, int index)
   {
     System.out.println("update the seqPanel colours");
     // repaint();
   }
 
+  @Override
   public void mouseMoved(MouseEvent evt)
   {
     int res = findRes(evt);
@@ -716,39 +800,38 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       mouseOverSequence(sequence, res, respos);
     }
 
-    StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: "
-            + sequence.getName());
+    StringBuilder text = new StringBuilder();
+    text.append("Sequence ").append(Integer.toString(seq + 1))
+            .append(" ID: ").append(sequence.getName());
 
-    Object obj = null;
+    String obj = null;
+    final String ch = String.valueOf(sequence.getCharAt(res));
     if (av.getAlignment().isNucleotide())
     {
-      obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res)
-              + "");
+      obj = ResidueProperties.nucleotideName.get(ch);
       if (obj != null)
       {
-        text.append(" Nucleotide: ");
+        text.append(" Nucleotide: ").append(obj);
       }
     }
     else
     {
-      obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");
+      obj = "X".equalsIgnoreCase(ch) ? "X" : ResidueProperties.aa2Triplet
+              .get(ch);
       if (obj != null)
       {
-        text.append("  Residue: ");
+        text.append(" Residue: ").append(obj);
       }
     }
 
     if (obj != null)
     {
-      if (obj != "")
-      {
-        text.append(obj + " (" + respos + ")");
-      }
+      text.append(" (").append(Integer.toString(respos)).append(")");
     }
 
     ap.alignFrame.statusBar.setText(text.toString());
 
-    StringBuffer tooltipText = new StringBuffer();
+    StringBuilder tooltipText = new StringBuilder();
     SequenceGroup[] groups = av.getAlignment().findAllGroups(sequence);
     if (groups != null)
     {
@@ -759,7 +842,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
           if (!groups[g].getName().startsWith("JTreeGroup")
                   && !groups[g].getName().startsWith("JGroup"))
           {
-            tooltipText.append(groups[g].getName() + " ");
+            tooltipText.append(groups[g].getName()).append(" ");
           }
           if (groups[g].getDescription() != null)
           {
@@ -818,13 +901,15 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       for (int i = 0; i < features.length; i++)
       {
         if (av.getFeaturesDisplayed() == null
-                || !av.getFeaturesDisplayed().isVisible(features[i].getType()))
+                || !av.getFeaturesDisplayed().isVisible(
+                        features[i].getType()))
         {
           continue;
         }
 
         if (features[i].featureGroup != null
-                && !seqCanvas.fr.checkGroupVisibility(features[i].featureGroup,false))
+                && !seqCanvas.fr.checkGroupVisibility(
+                        features[i].featureGroup, false))
         {
           continue;
         }
@@ -845,11 +930,20 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
   Tooltip tooltip;
 
+  /**
+   * set when the current UI interaction has resulted in a change that requires
+   * overview shading to be recalculated. this could be changed to something
+   * more expressive that indicates what actually has changed, so selective
+   * redraws can be applied
+   */
+  private boolean needOverviewUpdate; // TODO: refactor to avcontroller
+
+  @Override
   public void mouseDragged(MouseEvent evt)
   {
     if (mouseWheelPressed)
     {
-      int oldWidth = av.charWidth;
+      int oldWidth = av.getCharWidth();
 
       // Which is bigger, left-right or up-down?
       if (Math.abs(evt.getY() - lastMousePress.y) > Math.abs(evt.getX()
@@ -857,7 +951,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       {
         int fontSize = av.font.getSize();
 
-        if (evt.getY() < lastMousePress.y && av.charHeight > 1)
+        if (evt.getY() < lastMousePress.y && av.getCharHeight() > 1)
         {
           fontSize--;
         }
@@ -871,30 +965,32 @@ public class SeqPanel extends Panel implements MouseMotionListener,
           fontSize = 1;
         }
 
-        av.setFont(new Font(av.font.getName(), av.font.getStyle(), fontSize));
-        av.charWidth = oldWidth;
+        av.setFont(
+                new Font(av.font.getName(), av.font.getStyle(), fontSize),
+                true);
+        av.setCharWidth(oldWidth);
       }
       else
       {
-        if (evt.getX() < lastMousePress.x && av.charWidth > 1)
+        if (evt.getX() < lastMousePress.x && av.getCharWidth() > 1)
         {
-          av.charWidth--;
+          av.setCharWidth(av.getCharWidth() - 1);
         }
         else if (evt.getX() > lastMousePress.x)
         {
-          av.charWidth++;
+          av.setCharWidth(av.getCharWidth() + 1);
         }
 
-        if (av.charWidth < 1)
+        if (av.getCharWidth() < 1)
         {
-          av.charWidth = 1;
+          av.setCharWidth(1);
         }
       }
 
       ap.fontChanged();
 
       FontMetrics fm = getFontMetrics(av.getFont());
-      av.validCharWidth = fm.charWidth('M') <= av.charWidth;
+      av.validCharWidth = fm.charWidth('M') <= av.getCharWidth();
 
       lastMousePress = evt.getPoint();
 
@@ -960,15 +1056,18 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     StringBuffer message = new StringBuffer();
     if (groupEditing)
     {
-      message.append(MessageManager.getString("action.edit_group")).append(":");
+      message.append(MessageManager.getString("action.edit_group")).append(
+              ":");
       if (editCommand == null)
       {
-        editCommand = new EditCommand(MessageManager.getString("action.edit_group"));
+        editCommand = new EditCommand(
+                MessageManager.getString("action.edit_group"));
       }
     }
     else
     {
-      message.append(MessageManager.getString("label.edit_sequence")).append(" " + seq.getName());
+      message.append(MessageManager.getString("label.edit_sequence"))
+              .append(" " + seq.getName());
       String label = seq.getName();
       if (label.length() > 10)
       {
@@ -976,7 +1075,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       }
       if (editCommand == null)
       {
-        editCommand = new EditCommand(MessageManager.formatMessage("label.edit_params", new String[]{label}));
+        editCommand = new EditCommand(MessageManager.formatMessage(
+                "label.edit_params", new String[] { label }));
       }
     }
 
@@ -1039,8 +1139,10 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     if (av.hasHiddenColumns())
     {
       fixedColumns = true;
-      int y1 = av.getColumnSelection().getHiddenBoundaryLeft(startres);
-      int y2 = av.getColumnSelection().getHiddenBoundaryRight(startres);
+      int y1 = av.getAlignment().getHiddenColumns()
+              .getHiddenBoundaryLeft(startres);
+      int y2 = av.getAlignment().getHiddenColumns()
+              .getHiddenBoundaryRight(startres);
 
       if ((insertGap && startres > y1 && lastres < y1)
               || (!insertGap && startres < y2 && lastres > y2))
@@ -1111,8 +1213,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         {
           if (sg.getSize() == av.getAlignment().getHeight())
           {
-            if ((av.hasHiddenColumns() && startres < av
-                    .getColumnSelection().getHiddenBoundaryRight(startres)))
+            if ((av.hasHiddenColumns() && startres < av.getAlignment()
+                    .getHiddenColumns().getHiddenBoundaryRight(startres)))
             {
               endEditing();
               return;
@@ -1178,8 +1280,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
         else
         {
-          editCommand.appendEdit(Action.INSERT_GAP, groupSeqs,
-                  startres, startres - lastres, av.getAlignment(), true);
+          editCommand.appendEdit(Action.INSERT_GAP, groupSeqs, startres,
+                  startres - lastres, av.getAlignment(), true);
         }
       }
       else
@@ -1194,8 +1296,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
         else
         {
-          editCommand.appendEdit(Action.DELETE_GAP, groupSeqs,
-                  startres, lastres - startres, av.getAlignment(), true);
+          editCommand.appendEdit(Action.DELETE_GAP, groupSeqs, startres,
+                  lastres - startres, av.getAlignment(), true);
         }
 
       }
@@ -1210,14 +1312,14 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         {
           for (int j = lastres; j < startres; j++)
           {
-            insertChar(j, new SequenceI[]
-            { seq }, fixedRight);
+            insertChar(j, new SequenceI[] { seq }, fixedRight);
           }
         }
         else
         {
-          editCommand.appendEdit(Action.INSERT_GAP, new SequenceI[]
-          { seq }, lastres, startres - lastres, av.getAlignment(), true);
+          editCommand.appendEdit(Action.INSERT_GAP,
+                  new SequenceI[] { seq }, lastres, startres - lastres,
+                  av.getAlignment(), true);
         }
       }
       else
@@ -1232,8 +1334,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
               endEditing();
               break;
             }
-            deleteChar(startres, new SequenceI[]
-            { seq }, fixedRight);
+            deleteChar(startres, new SequenceI[] { seq }, fixedRight);
           }
         }
         else
@@ -1251,8 +1352,9 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
           if (max > 0)
           {
-            editCommand.appendEdit(Action.DELETE_GAP, new SequenceI[]
-            { seq }, startres, max, av.getAlignment(), true);
+            editCommand.appendEdit(Action.DELETE_GAP,
+                    new SequenceI[] { seq }, startres, max,
+                    av.getAlignment(), true);
           }
         }
       }
@@ -1290,16 +1392,16 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     editCommand.appendEdit(Action.DELETE_GAP, seq, blankColumn, 1,
             av.getAlignment(), true);
 
-    editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1,
-            av.getAlignment(), true);
+    editCommand.appendEdit(Action.INSERT_GAP, seq, j, 1, av.getAlignment(),
+            true);
 
   }
 
   void deleteChar(int j, SequenceI[] seq, int fixedColumn)
   {
 
-    editCommand.appendEdit(Action.DELETE_GAP, seq, j, 1,
-            av.getAlignment(), true);
+    editCommand.appendEdit(Action.DELETE_GAP, seq, j, 1, av.getAlignment(),
+            true);
 
     editCommand.appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1,
             av.getAlignment(), true);
@@ -1335,41 +1437,15 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
     stretchGroup = av.getSelectionGroup();
 
-    if (stretchGroup == null)
+    if (stretchGroup == null || !stretchGroup.contains(sequence, res))
     {
-      stretchGroup = av.getAlignment().findGroup(sequence);
-      if (stretchGroup != null && res > stretchGroup.getStartRes()
-              && res < stretchGroup.getEndRes())
+      stretchGroup = av.getAlignment().findGroup(sequence, res);
+      if (stretchGroup != null)
       {
+        // only update the current selection if the popup menu has a group to
+        // focus on
         av.setSelectionGroup(stretchGroup);
       }
-      else
-      {
-        stretchGroup = null;
-      }
-    }
-
-    else if (!stretchGroup.getSequences(null).contains(sequence)
-            || stretchGroup.getStartRes() > res
-            || stretchGroup.getEndRes() < res)
-    {
-      stretchGroup = null;
-
-      SequenceGroup[] allGroups = av.getAlignment().findAllGroups(sequence);
-
-      if (allGroups != null)
-      {
-        for (int i = 0; i < allGroups.length; i++)
-        {
-          if (allGroups[i].getStartRes() <= res
-                  && allGroups[i].getEndRes() >= res)
-          {
-            stretchGroup = allGroups[i];
-            break;
-          }
-        }
-      }
-      av.setSelectionGroup(stretchGroup);
     }
 
     // DETECT RIGHT MOUSE BUTTON IN AWT
@@ -1378,7 +1454,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       SequenceFeature[] allFeatures = findFeaturesAtRes(sequence,
               sequence.findPosition(res));
 
-      Vector links = null;
+      Vector<String> links = null;
       if (allFeatures != null)
       {
         for (int i = 0; i < allFeatures.length; i++)
@@ -1387,7 +1463,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
           {
             if (links == null)
             {
-              links = new Vector();
+              links = new Vector<String>();
             }
             for (int j = 0; j < allFeatures[i].links.size(); j++)
             {
@@ -1424,13 +1500,13 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
       if (av.getConservationSelected())
       {
-        SliderPanel.setConservationSlider(ap, av.getGlobalColourScheme(),
-                "Background");
+        SliderPanel.setConservationSlider(ap, av.getResidueShading(),
+                ap.getViewName());
       }
       if (av.getAbovePIDThreshold())
       {
-        SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(),
-                "Background");
+        SliderPanel.setPIDSliderSource(ap, av.getResidueShading(),
+                ap.getViewName());
       }
 
     }
@@ -1442,9 +1518,11 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     {
       return;
     }
-
-    stretchGroup.recalcConservation(); // always do this - annotation has own
-                                       // state
+    // always do this - annotation has own state
+    // but defer colourscheme update until hidden sequences are passed in
+    boolean vischange = stretchGroup.recalcConservation(true);
+    // here we rely on stretchGroup == av.getSelection()
+    needOverviewUpdate |= vischange && av.isSelectionDefinedGroup();
     if (stretchGroup.cs != null)
     {
       stretchGroup.cs.alignmentChanged(stretchGroup,
@@ -1455,17 +1533,18 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         SliderPanel.setConservationSlider(ap, stretchGroup.cs,
                 stretchGroup.getName());
       }
-      else
+      if (stretchGroup.cs.getThreshold() > 0)
       {
         SliderPanel.setPIDSliderSource(ap, stretchGroup.cs,
                 stretchGroup.getName());
       }
     }
+    PaintRefresher.Refresh(ap, av.getSequenceSetId());
+    ap.paintAlignment(needOverviewUpdate);
+    needOverviewUpdate = false;
     changeEndRes = false;
     changeStartRes = false;
     stretchGroup = null;
-    PaintRefresher.Refresh(ap, av.getSequenceSetId());
-    ap.paintAlignment(true);
     av.sendSelection();
   }
 
@@ -1517,6 +1596,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       if (res > (stretchGroup.getStartRes() - 1))
       {
         stretchGroup.setEndRes(res);
+        needOverviewUpdate |= av.isSelectionDefinedGroup();
       }
     }
     else if (changeStartRes)
@@ -1524,6 +1604,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       if (res < (stretchGroup.getEndRes() + 1))
       {
         stretchGroup.setStartRes(res);
+        needOverviewUpdate |= av.isSelectionDefinedGroup();
       }
     }
 
@@ -1557,6 +1638,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       if (stretchGroup.getSequences(null).contains(nextSeq))
       {
         stretchGroup.deleteSequence(seq, false);
+        needOverviewUpdate |= av.isSelectionDefinedGroup();
       }
       else
       {
@@ -1566,6 +1648,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
 
         stretchGroup.addSequence(nextSeq, false);
+        needOverviewUpdate |= av.isSelectionDefinedGroup();
       }
     }
 
@@ -1574,8 +1657,10 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       oldSeq = -1;
     }
 
-    if (res > av.endRes || res < av.startRes || y < av.startSeq
-            || y > av.endSeq)
+    if (res > av.getRanges().getEndRes()
+            || res < av.getRanges().getStartRes()
+            || y < av.getRanges().getStartSeq()
+            || y > av.getRanges().getEndSeq())
     {
       mouseExited(evt);
     }
@@ -1588,6 +1673,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     seqCanvas.repaint();
   }
 
+  @Override
   public void mouseEntered(MouseEvent e)
   {
     if (oldSeq < 0)
@@ -1602,6 +1688,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     }
   }
 
+  @Override
   public void mouseExited(MouseEvent e)
   {
     if (av.getWrapAlignment())
@@ -1661,6 +1748,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       running = false;
     }
 
+    @Override
     public void run()
     {
       running = true;
@@ -1670,13 +1758,15 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         if (evt != null)
         {
 
-          if (mouseDragging && evt.getY() < 0 && av.getStartSeq() > 0)
+          if (mouseDragging && evt.getY() < 0
+                  && av.getRanges().getStartSeq() > 0)
           {
             running = ap.scrollUp(true);
           }
 
           if (mouseDragging && evt.getY() >= getSize().height
-                  && av.getAlignment().getHeight() > av.getEndSeq())
+                  && av.getAlignment().getHeight() > av.getRanges()
+                          .getEndSeq())
           {
             running = ap.scrollUp(false);
           }
@@ -1705,24 +1795,38 @@ public class SeqPanel extends Panel implements MouseMotionListener,
   /**
    * modify current selection according to a received message.
    */
+  @Override
   public void selection(SequenceGroup seqsel, ColumnSelection colsel,
-          SelectionSource source)
+          HiddenColumns hidden, SelectionSource source)
   {
     // TODO: fix this hack - source of messages is align viewport, but SeqPanel
     // handles selection messages...
     // TODO: extend config options to allow user to control if selections may be
     // shared between viewports.
     if (av != null
-            && (av == source || !av.followSelection || (source instanceof AlignViewport && ((AlignViewport) source)
+            && (av == source || !av.followSelection || (source instanceof AlignViewport && ((AlignmentViewport) source)
                     .getSequenceSetId().equals(av.getSequenceSetId()))))
     {
       return;
     }
+
+    /*
+     * Check for selection in a view of which this one is a dna/protein
+     * complement.
+     */
+    if (selectionFromTranslation(seqsel, colsel, hidden, source))
+    {
+      return;
+    }
+
     // do we want to thread this ? (contention with seqsel and colsel locks, I
     // suspect)
-    // rules are: colsel is copied if there is a real intersection between
-    // sequence selection
-    boolean repaint = false, copycolsel = true;
+    /*
+     * only copy colsel if there is a real intersection between
+     * sequence selection and this panel's alignment
+     */
+    boolean repaint = false;
+    boolean copycolsel = false;
     if (av.getSelectionGroup() == null || !av.isSelectionGroupChanged(true))
     {
       SequenceGroup sgroup = null;
@@ -1739,11 +1843,9 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
         sgroup = seqsel.intersect(av.getAlignment(),
                 (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null);
-        if ((sgroup == null || sgroup.getSize() == 0)
-                && (colsel == null || colsel.size() == 0))
+        if ((sgroup != null && sgroup.getSize() > 0))
         {
-          // don't copy columns if the region didn't intersect.
-          copycolsel = false;
+          copycolsel = true;
         }
       }
       if (sgroup != null && sgroup.getSize() > 0)
@@ -1762,7 +1864,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     {
       // the current selection is unset or from a previous message
       // so import the new colsel.
-      if (colsel == null || colsel.size() == 0)
+      if (colsel == null || colsel.isEmpty())
       {
         if (av.getColumnSelection() != null)
         {
@@ -1778,15 +1880,16 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
         else
         {
-          av.getColumnSelection().setElementsFrom(colsel);
+          av.getColumnSelection().setElementsFrom(colsel,
+                  av.getAlignment().getHiddenColumns());
         }
       }
       repaint |= av.isColSelChanged(true);
     }
     if (copycolsel
             && av.hasHiddenColumns()
-            && (av.getColumnSelection() == null || av.getColumnSelection()
-                    .getHiddenColumns() == null))
+            && (av.getColumnSelection() == null || av.getAlignment()
+                    .getHiddenColumns().getHiddenRegions() == null))
     {
       System.err.println("Bad things");
     }
@@ -1806,8 +1909,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
   public void scrollTo(int row, int column)
   {
 
-    row = row < 0 ? ap.av.startSeq : row;
-    column = column < 0 ? ap.av.startRes : column;
+    row = row < 0 ? ap.av.getRanges().getStartSeq() : row;
+    column = column < 0 ? ap.av.getRanges().getStartRes() : column;
     ap.scrollTo(column, column, row, true, true);
   }
 
@@ -1819,8 +1922,9 @@ public class SeqPanel extends Panel implements MouseMotionListener,
   public void scrollToRow(int row)
   {
 
-    row = row < 0 ? ap.av.startSeq : row;
-    ap.scrollTo(ap.av.startRes, ap.av.startRes, row, true, true);
+    row = row < 0 ? ap.av.getRanges().getStartSeq() : row;
+    ap.scrollTo(ap.av.getRanges().getStartRes(), ap.av.getRanges()
+            .getStartRes(), row, true, true);
   }
 
   /**
@@ -1831,8 +1935,56 @@ public class SeqPanel extends Panel implements MouseMotionListener,
   public void scrollToColumn(int column)
   {
 
-    column = column < 0 ? ap.av.startRes : column;
-    ap.scrollTo(column, column, ap.av.startSeq, true, true);
+    column = column < 0 ? ap.av.getRanges().getStartRes() : column;
+    ap.scrollTo(column, column, ap.av.getRanges().getStartSeq(), true, true);
+  }
+
+  /**
+   * If this panel is a cdna/protein translation view of the selection source,
+   * tries to map the source selection to a local one, and returns true. Else
+   * returns false.
+   * 
+   * @param seqsel
+   * @param colsel
+   * @param source
+   */
+  protected boolean selectionFromTranslation(SequenceGroup seqsel,
+          ColumnSelection colsel, HiddenColumns hidden,
+          SelectionSource source)
+  {
+    if (!(source instanceof AlignViewportI))
+    {
+      return false;
+    }
+    final AlignViewportI sourceAv = (AlignViewportI) source;
+    if (sourceAv.getCodingComplement() != av
+            && av.getCodingComplement() != sourceAv)
+    {
+      return false;
+    }
+
+    /*
+     * Map sequence selection
+     */
+    SequenceGroup sg = MappingUtils.mapSequenceGroup(seqsel, sourceAv, av);
+    av.setSelectionGroup(sg);
+    av.isSelectionGroupChanged(true);
+
+    /*
+     * Map column selection
+     */
+    // ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,
+    // av);
+    ColumnSelection cs = new ColumnSelection();
+    HiddenColumns hs = new HiddenColumns();
+    MappingUtils.mapColumnSelection(colsel, hidden, sourceAv, av, cs, hs);
+    av.setColumnSelection(cs);
+    av.getAlignment().setHiddenColumns(hs);
+
+    ap.scalePanelHolder.repaint();
+    ap.repaint();
+
+    return true;
   }
 
 }