Merge branch 'develop' into features/JAL-2360colourSchemeApplicability
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 20 Jan 2017 09:47:20 +0000 (09:47 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 20 Jan 2017 09:47:20 +0000 (09:47 +0000)
Conflicts:
src/jalview/appletgui/APopupMenu.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/PopupMenu.java
src/jalview/jbgui/GAlignFrame.java

17 files changed:
resources/lang/Messages.properties
src/jalview/appletgui/APopupMenu.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/RedundancyPanel.java
src/jalview/appletgui/ScalePanel.java
src/jalview/appletgui/SliderPanel.java
src/jalview/datamodel/ColumnSelection.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/RedundancyPanel.java
src/jalview/gui/ScalePanel.java
src/jalview/gui/SliderPanel.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/jbgui/GSliderPanel.java
src/jalview/util/ParseHtmlBodyAndLinks.java
test/jalview/datamodel/ColumnSelectionTest.java

index 296a23f..c451f07 100644 (file)
@@ -325,7 +325,7 @@ label.size = Size:
 label.style = Style:
 label.calculating = Calculating....
 label.modify_conservation_visibility = Modify conservation visibility
-label.colour_residues_above_occurence = Colour residues above % occurence
+label.colour_residues_above_occurrence = Colour residues above % occurrence
 label.set_this_label_text = set this label text
 label.sequences_from = Sequences from {0}
 label.successfully_loaded_file  = Successfully loaded file {0}
index 4eaa18a..1f3d318 100644 (file)
@@ -111,8 +111,14 @@ public class APopupMenu extends java.awt.PopupMenu implements
 
   protected CheckboxMenuItem abovePIDColour = new CheckboxMenuItem();
 
+  MenuItem modifyPID = new MenuItem();
+
   protected CheckboxMenuItem conservationColour = new CheckboxMenuItem();
 
+  MenuItem modifyConservation = new MenuItem();
+
+  MenuItem noColourmenuItem = new MenuItem();
+
   final AlignmentPanel ap;
 
   MenuItem unGroupMenuItem = new MenuItem();
@@ -255,6 +261,8 @@ public class APopupMenu extends java.awt.PopupMenu implements
         {
           abovePIDColour.setState(sg.cs.getThreshold() > 0);
           conservationColour.setState(sg.cs.conservationApplied());
+          modifyPID.setEnabled(abovePIDColour.getState());
+          modifyConservation.setEnabled(conservationColour.getState());
         }
       }
       setSelectedColour(sg.cs);
@@ -634,6 +642,14 @@ public class APopupMenu extends java.awt.PopupMenu implements
     {
       userDefinedColour_actionPerformed();
     }
+    else if (source == modifyConservation)
+    {
+      conservationMenuItem_itemStateChanged();
+    }
+    else if (source == modifyPID)
+    {
+      abovePIDColour_itemStateChanged();
+    }
     else if (source == unGroupMenuItem)
     {
       unGroupMenuItem_actionPerformed();
@@ -899,14 +915,14 @@ public class APopupMenu extends java.awt.PopupMenu implements
   void addPDB()
   {
     Vector<PDBEntry> pdbs = seq.getAllPDBEntries();
-    if (pdbs != null&& !pdbs.isEmpty())
+    if (pdbs != null && !pdbs.isEmpty())
     {
       PDBEntry entry = pdbs.firstElement();
 
       if (ap.av.applet.jmolAvailable)
       {
-        new jalview.appletgui.AppletJmol(entry, new SequenceI[] { seq },
-                null, ap, DataSourceType.URL);
+        new AppletJmol(entry, new SequenceI[] { seq }, null, ap,
+                DataSourceType.URL);
       }
       else
       {
@@ -922,7 +938,7 @@ public class APopupMenu extends java.awt.PopupMenu implements
       cap.setPDBImport(seq);
       Frame frame = new Frame();
       frame.add(cap);
-      jalview.bin.JalviewLite.addFrame(frame, MessageManager.formatMessage(
+      JalviewLite.addFrame(frame, MessageManager.formatMessage(
               "label.paste_pdb_file_for_sequence",
               new Object[] { seq.getName() }), 400, 300);
     }
@@ -997,8 +1013,10 @@ public class APopupMenu extends java.awt.PopupMenu implements
     colourMenu.add(purinePyrimidineColour);
     colourMenu.add(userDefinedColour);
     colourMenu.addSeparator();
-    colourMenu.add(abovePIDColour);
     colourMenu.add(conservationColour);
+    colourMenu.add(modifyConservation);
+    colourMenu.add(abovePIDColour);
+    colourMenu.add(modifyPID);
 
     noColour.setLabel(MessageManager.getString("label.none"));
     noColour.addItemListener(this);
@@ -1064,9 +1082,15 @@ public class APopupMenu extends java.awt.PopupMenu implements
     abovePIDColour.setLabel(MessageManager
             .getString("label.above_identity_threshold"));
     abovePIDColour.addItemListener(this);
+    modifyPID.setLabel(MessageManager
+            .getString("label.modify_identity_threshold"));
+    modifyPID.addActionListener(this);
     conservationColour.setLabel(MessageManager
-            .getString("label.conservation"));
+            .getString("action.by_conservation"));
     conservationColour.addItemListener(this);
+    modifyConservation.setLabel(MessageManager
+            .getString("label.modify_conservation_threshold"));
+    modifyConservation.addActionListener(this);
 
     editMenu.add(copy);
     copy.addActionListener(this);
@@ -1197,11 +1221,11 @@ public class APopupMenu extends java.awt.PopupMenu implements
     else
     // remove PIDColouring
     {
+      SliderPanel.hidePIDSlider();
       sg.cs.setThreshold(0, ap.av.isIgnoreGapsConsensus());
     }
-
+    modifyPID.setEnabled(abovePIDColour.getState());
     refresh();
-
   }
 
   protected void userDefinedColour_actionPerformed()
@@ -1256,9 +1280,10 @@ public class APopupMenu extends java.awt.PopupMenu implements
     else
     // remove ConservationColouring
     {
+      SliderPanel.hideConservationSlider();
       sg.cs.setConservation(null);
     }
-
+    modifyConservation.setEnabled(conservationColour.getState());
     refresh();
   }
 
index 620ceb2..cfd3d00 100644 (file)
@@ -2681,26 +2681,43 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
 
   protected void conservationMenuItem_actionPerformed()
   {
-    viewport.setConservationSelected(conservationMenuItem.getState());
+    boolean selected = conservationMenuItem.getState();
+    modifyConservation.setEnabled(selected);
+    viewport.setConservationSelected(selected);
 
-    viewport.setAbovePIDThreshold(false);
-    abovePIDThreshold.setState(false);
+    // viewport.setAbovePIDThreshold(false);
+    // abovePIDThreshold.setState(false);
 
     changeColour(viewport.getGlobalColourScheme());
 
-    modifyConservation_actionPerformed();
+    if (selected)
+    {
+      modifyConservation_actionPerformed();
+    }
+    else
+    {
+      SliderPanel.hideConservationSlider();
+    }
   }
 
   public void abovePIDThreshold_actionPerformed()
   {
-    viewport.setAbovePIDThreshold(abovePIDThreshold.getState());
-
-    conservationMenuItem.setState(false);
-    viewport.setConservationSelected(false);
+    boolean selected = abovePIDThreshold.getState();
+    modifyPID.setEnabled(selected);
+    viewport.setAbovePIDThreshold(selected);
+    // conservationMenuItem.setState(false);
+    // viewport.setConservationSelected(false);
 
     changeColour(viewport.getGlobalColourScheme());
 
-    modifyPID_actionPerformed();
+    if (selected)
+    {
+      modifyPID_actionPerformed();
+    }
+    else
+    {
+      SliderPanel.hidePIDSlider();
+    }
   }
 
   public void sortPairwiseMenuItem_actionPerformed()
@@ -3528,9 +3545,11 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     nucleotideColour.addActionListener(this);
     modifyPID.setLabel(MessageManager
             .getString("label.modify_identity_threshold"));
+    modifyPID.setEnabled(abovePIDThreshold.getState());
     modifyPID.addActionListener(this);
     modifyConservation.setLabel(MessageManager
             .getString("label.modify_conservation_threshold"));
+    modifyConservation.setEnabled(conservationMenuItem.getState());
     modifyConservation.addActionListener(this);
     annotationColour.setLabel(MessageManager
             .getString("action.by_annotation"));
index 4aea837..6be416c 100644 (file)
@@ -74,9 +74,10 @@ public class RedundancyPanel extends SliderPanel implements Runnable,
 
     slider.addAdjustmentListener(new AdjustmentListener()
     {
+      @Override
       public void adjustmentValueChanged(AdjustmentEvent evt)
       {
-        valueField.setText(slider.getValue() + "");
+        valueField.setText(String.valueOf(slider.getValue()));
         sliderValueChanged();
       }
     });
@@ -104,6 +105,7 @@ public class RedundancyPanel extends SliderPanel implements Runnable,
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public void run()
   {
     label.setText(MessageManager.getString("label.calculating"));
@@ -172,6 +174,7 @@ public class RedundancyPanel extends SliderPanel implements Runnable,
 
   }
 
+  @Override
   public void applyButton_actionPerformed()
   {
     Vector del = new Vector();
@@ -230,6 +233,7 @@ public class RedundancyPanel extends SliderPanel implements Runnable,
 
   }
 
+  @Override
   public void undoButton_actionPerformed()
   {
     CommandI command = (CommandI) historyList.pop();
@@ -263,31 +267,38 @@ public class RedundancyPanel extends SliderPanel implements Runnable,
     }
   }
 
+  @Override
   public void windowOpened(WindowEvent evt)
   {
   }
 
+  @Override
   public void windowClosing(WindowEvent evt)
   {
     ap.idPanel.idCanvas.setHighlighted(null);
   }
 
+  @Override
   public void windowClosed(WindowEvent evt)
   {
   }
 
+  @Override
   public void windowActivated(WindowEvent evt)
   {
   }
 
+  @Override
   public void windowDeactivated(WindowEvent evt)
   {
   }
 
+  @Override
   public void windowIconified(WindowEvent evt)
   {
   }
 
+  @Override
   public void windowDeiconified(WindowEvent evt)
   {
   }
index 5a156fa..ed07b63 100755 (executable)
@@ -264,72 +264,30 @@ public class ScalePanel extends Panel implements MouseMotionListener,
     av.sendSelection();
   }
 
+  /**
+   * Action on dragging the mouse in the scale panel is to expand or shrink the
+   * selection group range (including any hidden columns that it spans)
+   * 
+   * @param evt
+   */
   @Override
   public void mouseDragged(MouseEvent evt)
   {
     mouseDragging = true;
+    ColumnSelection cs = av.getColumnSelection();
 
     int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
-    if (res < 0)
-    {
-      res = 0;
-    }
-
-    if (av.hasHiddenColumns())
-    {
-      res = av.getColumnSelection().adjustForHiddenColumns(res);
-    }
-
-    if (res > av.getAlignment().getWidth())
-    {
-      res = av.getAlignment().getWidth() - 1;
-    }
-
-    if (res < min)
-    {
-      min = res;
-    }
-
-    if (res > max)
-    {
-      max = res;
-    }
+    res = Math.max(0, res);
+    res = cs.adjustForHiddenColumns(res);
+    res = Math.min(res, av.getAlignment().getWidth() - 1);
+    min = Math.min(res, min);
+    max = Math.max(res, max);
 
     SequenceGroup sg = av.getSelectionGroup();
-
     if (sg != null)
     {
       stretchingGroup = true;
-
-      if (!av.getColumnSelection().contains(res))
-      {
-        av.getColumnSelection().addElement(res);
-      }
-
-      if (res > sg.getStartRes())
-      {
-        sg.setEndRes(res);
-      }
-      if (res < sg.getStartRes())
-      {
-        sg.setStartRes(res);
-      }
-
-      int col;
-      for (int i = min; i <= max; i++)
-      {
-        col = av.getColumnSelection().adjustForHiddenColumns(i);
-
-        if ((col < sg.getStartRes()) || (col > sg.getEndRes()))
-        {
-          av.getColumnSelection().removeElement(col);
-        }
-        else
-        {
-          av.getColumnSelection().addElement(col);
-        }
-      }
-
+      cs.stretchGroup(res, sg, min, max);
       ap.paintAlignment(false);
     }
   }
index 35c2a22..5179eb0 100644 (file)
@@ -38,6 +38,8 @@ import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.AdjustmentEvent;
 import java.awt.event.AdjustmentListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 import java.awt.event.WindowAdapter;
@@ -106,6 +108,7 @@ public class SliderPanel extends Panel implements ActionListener,
               conservationSlider.getTitle(), 420, 100);
       conservationSlider.addWindowListener(new WindowAdapter()
       {
+        @Override
         public void windowClosing(WindowEvent e)
         {
           conservationSlider = null;
@@ -122,7 +125,7 @@ public class SliderPanel extends Panel implements ActionListener,
     SliderPanel pid = null;
     if (PIDSlider == null)
     {
-      pid = new SliderPanel(ap, 50, false, cs);
+      pid = new SliderPanel(ap, cs.getThreshold(), false, cs);
       PIDSlider = new Frame();
       PIDSlider.add(pid);
     }
@@ -165,6 +168,7 @@ public class SliderPanel extends Panel implements ActionListener,
               420, 100);
       PIDSlider.addWindowListener(new WindowAdapter()
       {
+        @Override
         public void windowClosing(WindowEvent e)
         {
           PIDSlider = null;
@@ -174,6 +178,29 @@ public class SliderPanel extends Panel implements ActionListener,
 
   }
 
+  /**
+   * Hides the PID slider panel if it is shown
+   */
+  public static void hidePIDSlider()
+  {
+    if (PIDSlider != null)
+    {
+      PIDSlider.setVisible(false);
+    }
+    PIDSlider = null;
+  }
+
+  /**
+   * Hides the Conservation slider panel if it is shown
+   */
+  public static void hideConservationSlider()
+  {
+    if (conservationSlider != null)
+    {
+      conservationSlider.setVisible(false);
+    }
+    conservationSlider = null;
+  }
   public SliderPanel(AlignmentPanel ap, int value, boolean forConserve,
           ColourSchemeI cs)
   {
@@ -200,7 +227,7 @@ public class SliderPanel extends Panel implements ActionListener,
     else
     {
       label.setText(MessageManager
-              .getString("label.colour_residues_above_occurence"));
+              .getString("label.colour_residues_above_occurrence"));
       slider.setMinimum(0);
       slider.setMaximum(100 + slider.getVisibleAmount());
       slider.setBlockIncrement(1);
@@ -261,6 +288,7 @@ public class SliderPanel extends Panel implements ActionListener,
     allGroupsCheck.setEnabled(b);
   }
 
+  @Override
   public void actionPerformed(ActionEvent evt)
   {
     if (evt.getSource() == applyButton)
@@ -277,6 +305,7 @@ public class SliderPanel extends Panel implements ActionListener,
     }
   }
 
+  @Override
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
     valueField.setText(slider.getValue() + "");
@@ -287,11 +316,11 @@ public class SliderPanel extends Panel implements ActionListener,
   {
     try
     {
-      int i = Integer.parseInt(valueField.getText());
+      int i = Integer.valueOf(valueField.getText());
       slider.setValue(i);
-    } catch (Exception ex)
+    } catch (NumberFormatException ex)
     {
-      valueField.setText(slider.getValue() + "");
+      valueField.setText(String.valueOf(slider.getValue()));
     }
   }
 
@@ -344,6 +373,16 @@ public class SliderPanel extends Panel implements ActionListener,
     valueField.setText("   ");
     valueField.addActionListener(this);
     valueField.setColumns(3);
+    valueField.addFocusListener(new FocusAdapter()
+    {
+      @Override
+      public void focusLost(FocusEvent e)
+      {
+        valueField_actionPerformed();
+        valueChanged(slider.getValue());
+      }
+    });
+    
     label.setFont(new java.awt.Font("Verdana", 0, 11));
     label.setText(MessageManager.getString("label.set_this_label_text"));
     jPanel1.setLayout(borderLayout1);
@@ -381,23 +420,28 @@ public class SliderPanel extends Panel implements ActionListener,
   {
   }
 
+  @Override
   public void mousePressed(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
     ap.paintAlignment(true);
   }
 
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseEntered(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseExited(MouseEvent evt)
   {
   }
index d651c0b..98a7fe2 100644 (file)
@@ -866,6 +866,24 @@ public class ColumnSelection
          */
         region[0] = Math.min(region[0], start);
         region[1] = Math.max(region[1], end);
+
+        /*
+         * also update or remove any subsequent ranges 
+         * that are overlapped
+         */
+        while (i < hiddenColumns.size() - 1)
+        {
+          int[] nextRegion = hiddenColumns.get(i + 1);
+          if (nextRegion[0] > end + 1)
+          {
+            /*
+             * gap to next hidden range - no more to update
+             */
+            break;
+          }
+          region[1] = Math.max(nextRegion[1], end);
+          hiddenColumns.remove(i + 1);
+        }
         return;
       }
     }
@@ -1813,4 +1831,55 @@ public class ColumnSelection
     return changed;
   }
 
+  /**
+   * Adjusts column selections, and the given selection group, to match the
+   * range of a stretch (e.g. mouse drag) operation
+   * <p>
+   * Method refactored from ScalePanel.mouseDragged
+   * 
+   * @param res
+   *          current column position, adjusted for hidden columns
+   * @param sg
+   *          current selection group
+   * @param min
+   *          start position of the stretch group
+   * @param max
+   *          end position of the stretch group
+   */
+  public void stretchGroup(int res, SequenceGroup sg, int min, int max)
+  {
+    if (!contains(res))
+    {
+      addElement(res);
+    }
+
+    if (res > sg.getStartRes())
+    {
+      // expand selection group to the right
+      sg.setEndRes(res);
+    }
+    if (res < sg.getStartRes())
+    {
+      // expand selection group to the left
+      sg.setStartRes(res);
+    }
+
+    /*
+     * expand or shrink column selection to match the
+     * range of the drag operation
+     */
+    for (int col = min; col <= max; col++)
+    {
+      if (col < sg.getStartRes() || col > sg.getEndRes())
+      {
+        // shrinking drag - remove from selection
+        removeElement(col);
+      }
+      else
+      {
+        // expanding drag - add to selection
+        addElement(col);
+      }
+    }
+  }
 }
index 514d486..40ab6bb 100644 (file)
@@ -845,7 +845,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     showReverse.setVisible(nucleotide);
     showReverseComplement.setVisible(nucleotide);
     conservationMenuItem.setEnabled(!nucleotide);
-    modifyConservation.setEnabled(!nucleotide);
+    modifyConservation.setEnabled(!nucleotide
+            && conservationMenuItem.isSelected());
     showGroupConservation.setEnabled(!nucleotide);
 
     showComplementMenuItem.setText(nucleotide ? MessageManager
@@ -876,7 +877,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     padGapsMenuitem.setSelected(av.isPadGaps());
     colourTextMenuItem.setSelected(av.isShowColourText());
     abovePIDThreshold.setSelected(av.getAbovePIDThreshold());
+    modifyPID.setEnabled(abovePIDThreshold.isSelected());
     conservationMenuItem.setSelected(av.getConservationSelected());
+    modifyConservation.setEnabled(conservationMenuItem.isSelected());
     seqLimits.setSelected(av.getShowJVSuffix());
     idRightAlign.setSelected(av.isRightAlignIds());
     centreColumnLabelsMenuItem.setState(av.isCentreColumnLabels());
@@ -3400,41 +3403,45 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
+   * Action on selecting or deselecting (Colour) By Conservation
    */
   @Override
   protected void conservationMenuItem_actionPerformed()
   {
-    viewport.setConservationSelected(conservationMenuItem.isSelected());
-
-    viewport.setAbovePIDThreshold(false);
-    abovePIDThreshold.setSelected(false);
+    boolean selected = conservationMenuItem.isSelected();
+    modifyConservation.setEnabled(selected);
+    viewport.setConservationSelected(selected);
 
     changeColour(viewport.getGlobalColourScheme());
-
-    modifyConservation_actionPerformed();
+    if (selected)
+    {
+      modifyConservation_actionPerformed();
+    }
+    else
+    {
+      SliderPanel.hideConservationSlider();
+    }
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
+   * Action on selecting or deselecting (Colour) Above PID Threshold
    */
   @Override
   public void abovePIDThreshold_actionPerformed()
   {
-    viewport.setAbovePIDThreshold(abovePIDThreshold.isSelected());
-
-    conservationMenuItem.setSelected(false);
-    viewport.setConservationSelected(false);
+    boolean selected = abovePIDThreshold.isSelected();
+    modifyPID.setEnabled(selected);
+    viewport.setAbovePIDThreshold(selected);
 
     changeColour(viewport.getGlobalColourScheme());
-
-    modifyPID_actionPerformed();
+    if (selected)
+    {
+      modifyPID_actionPerformed();
+    }
+    else
+    {
+      SliderPanel.hidePIDSlider();
+    }
   }
 
   /**
index 12e6d42..7057c2a 100644 (file)
@@ -4420,10 +4420,12 @@ public class Jalview2XML
 
     af.viewport.setShowAnnotation(view.getShowAnnotation());
     af.viewport.setAbovePIDThreshold(view.getPidSelected());
+    af.viewport.setThreshold(view.getPidThreshold());
 
     af.viewport.setColourText(view.getShowColourText());
 
     af.viewport.setConservationSelected(view.getConservationSelected());
+    af.viewport.setIncrement(view.getConsThreshold());
     af.viewport.setShowJVSuffix(view.getShowFullId());
     af.viewport.setRightAlignIds(view.getRightAlignIds());
     af.viewport.setFont(
@@ -4473,7 +4475,6 @@ public class Jalview2XML
 
       if (cs != null)
       {
-        cs.setThreshold(view.getPidThreshold(), true);
         cs.setConsensus(af.viewport.getSequenceConsensusHash());
       }
     }
index ac2fbc2..310266e 100644 (file)
@@ -87,8 +87,12 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
   protected JCheckBoxMenuItem abovePIDColour = new JCheckBoxMenuItem();
 
+  protected JMenuItem modifyPID = new JMenuItem();
+
   protected JCheckBoxMenuItem conservationMenuItem = new JCheckBoxMenuItem();
 
+  protected JMenuItem modifyConservation = new JMenuItem();
+
   AlignmentPanel ap;
 
   JMenu sequenceMenu = new JMenu();
@@ -434,6 +438,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
           abovePIDColour.setSelected(true);
         }
       }
+      modifyConservation.setEnabled(conservationMenuItem.isSelected());
+      modifyPID.setEnabled(abovePIDColour.isSelected());
       displayNonconserved.setSelected(sg.getShowNonconserved());
       showText.setSelected(sg.getDisplayText());
       showColourText.setSelected(sg.getColourText());
@@ -557,8 +563,6 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
   }
 
-
-
   /**
    * Add annotation types to 'Show annotations' and/or 'Hide annotations' menus.
    * "All" is added first, followed by a separator. Then add any annotation
@@ -982,6 +986,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         sequenceSelectionDetails_actionPerformed();
       }
     });
+
     unGroupMenuItem
             .setText(MessageManager.getString("action.remove_group"));
     unGroupMenuItem.addActionListener(new ActionListener()
@@ -1231,7 +1236,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     jMenu1.add(outline);
     jMenu1.add(displayNonconserved);
   }
-
+  
   /**
    * Constructs the entries for the colour menu
    */
@@ -1247,6 +1252,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         textColour_actionPerformed();
       }
     });
+
     abovePIDColour.setText(MessageManager
             .getString("label.above_identity_threshold"));
     abovePIDColour.addActionListener(new ActionListener()
@@ -1258,6 +1264,17 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       }
     });
 
+    modifyPID.setText(MessageManager
+            .getString("label.modify_identity_threshold"));
+    modifyPID.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        modifyPID_actionPerformed();
+      }
+    });
+
     conservationMenuItem.setText(MessageManager
             .getString("action.by_conservation"));
     conservationMenuItem.addActionListener(new ActionListener()
@@ -1268,6 +1285,17 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         conservationMenuItem_actionPerformed();
       }
     });
+
+    modifyConservation.setText(MessageManager
+            .getString("label.modify_conservation_threshold"));
+    modifyConservation.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        modifyConservation_actionPerformed();
+      }
+    });
   }
 
   /**
@@ -1292,7 +1320,33 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
 
     colourMenu.addSeparator();
     colourMenu.add(conservationMenuItem);
+    colourMenu.add(modifyConservation);
     colourMenu.add(abovePIDColour);
+    colourMenu.add(modifyPID);
+  }
+
+  protected void modifyConservation_actionPerformed()
+  {
+    SequenceGroup sg = getGroup();
+    if (sg.cs != null)
+    {
+      SliderPanel.setConservationSlider(ap, sg.cs, sg.getName());
+      SliderPanel.showConservationSlider();
+    }
+  }
+
+  protected void modifyPID_actionPerformed()
+  {
+    SequenceGroup sg = getGroup();
+    if (sg.cs != null)
+    {
+      // int threshold = SliderPanel.setPIDSliderSource(ap, sg.cs, getGroup()
+      // .getName());
+      // sg.cs.setThreshold(threshold, ap.av.isIgnoreGapsConsensus());
+      SliderPanel.setPIDSliderSource(ap, sg.cs, getGroup()
+              .getName());
+      SliderPanel.showPIDSlider();
+    }
   }
 
   /**
@@ -1484,7 +1538,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       return;
     }
 
-    if (abovePIDColour.isSelected())
+    boolean selected = abovePIDColour.isSelected();
+    if (selected)
     {
       sg.cs.setConsensus(AAFrequency.calculate(
               sg.getSequences(ap.av.getHiddenRepSequences()),
@@ -1501,7 +1556,9 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     // remove PIDColouring
     {
       sg.cs.setThreshold(0, ap.av.isIgnoreGapsConsensus());
+      SliderPanel.hidePIDSlider();
     }
+    modifyPID.setEnabled(selected);
 
     refresh();
   }
@@ -1532,7 +1589,8 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       return;
     }
 
-    if (conservationMenuItem.isSelected())
+    boolean selected = conservationMenuItem.isSelected();
+    if (selected)
     {
       // JBPNote: Conservation name shouldn't be i18n translated
       Conservation c = new Conservation("Group", sg.getSequences(ap.av
@@ -1551,7 +1609,9 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
     // remove ConservationColouring
     {
       sg.cs.setConservation(null);
+      SliderPanel.hideConservationSlider();
     }
+    modifyConservation.setEnabled(selected);
 
     refresh();
   }
index a9d2690..cbbcf70 100755 (executable)
@@ -82,6 +82,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable
 
     slider.addChangeListener(new ChangeListener()
     {
+      @Override
       public void stateChanged(ChangeEvent evt)
       {
         valueField.setText(slider.getValue() + "");
@@ -105,6 +106,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable
             false);
     frame.addInternalFrameListener(new InternalFrameAdapter()
     {
+      @Override
       public void internalFrameClosing(InternalFrameEvent evt)
       {
         ap.getIdPanel().getIdCanvas().setHighlighted(null);
@@ -125,6 +127,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable
    * 
    * @return DOCUMENT ME!
    */
+  @Override
   public void run()
   {
     JProgressBar progress = new JProgressBar();
@@ -207,6 +210,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void applyButton_actionPerformed(ActionEvent e)
   {
     Vector del = new Vector();
@@ -271,6 +275,7 @@ public class RedundancyPanel extends GSliderPanel implements Runnable
    * @param e
    *          DOCUMENT ME!
    */
+  @Override
   public void undoButton_actionPerformed(ActionEvent e)
   {
     if (historyList == null || historyList.isEmpty())
@@ -297,22 +302,4 @@ public class RedundancyPanel extends GSliderPanel implements Runnable
     }
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  public void valueField_actionPerformed(ActionEvent e)
-  {
-    try
-    {
-      int i = Integer.parseInt(valueField.getText());
-      slider.setValue(i);
-    } catch (Exception ex)
-    {
-      valueField.setText(slider.getValue() + "");
-    }
-  }
-
 }
index 0aa2459..8961f21 100755 (executable)
@@ -326,77 +326,29 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
   }
 
   /**
-   * DOCUMENT ME!
+   * Action on dragging the mouse in the scale panel is to expand or shrink the
+   * selection group range (including any hidden columns that it spans)
    * 
    * @param evt
-   *          DOCUMENT ME!
    */
   @Override
   public void mouseDragged(MouseEvent evt)
   {
     mouseDragging = true;
+    ColumnSelection cs = av.getColumnSelection();
 
     int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
-    if (res < 0)
-    {
-      res = 0;
-    }
-
-    if (av.hasHiddenColumns())
-    {
-      res = av.getColumnSelection().adjustForHiddenColumns(res);
-    }
-
-    if (res >= av.getAlignment().getWidth())
-    {
-      res = av.getAlignment().getWidth() - 1;
-    }
-
-    if (res < min)
-    {
-      min = res;
-    }
-
-    if (res > max)
-    {
-      max = res;
-    }
+    res = Math.max(0, res);
+    res = cs.adjustForHiddenColumns(res);
+    res = Math.min(res, av.getAlignment().getWidth() - 1);
+    min = Math.min(res, min);
+    max = Math.max(res, max);
 
     SequenceGroup sg = av.getSelectionGroup();
-
     if (sg != null)
     {
       stretchingGroup = true;
-
-      if (!av.getColumnSelection().contains(res))
-      {
-        av.getColumnSelection().addElement(res);
-      }
-
-      if (res > sg.getStartRes())
-      {
-        sg.setEndRes(res);
-      }
-      if (res < sg.getStartRes())
-      {
-        sg.setStartRes(res);
-      }
-
-      int col;
-      for (int i = min; i <= max; i++)
-      {
-        col = i; // av.getColumnSelection().adjustForHiddenColumns(i);
-
-        if ((col < sg.getStartRes()) || (col > sg.getEndRes()))
-        {
-          av.getColumnSelection().removeElement(col);
-        }
-        else
-        {
-          av.getColumnSelection().addElement(col);
-        }
-      }
-
+      cs.stretchGroup(res, sg, min, max);
       ap.paintAlignment(false);
     }
   }
@@ -424,6 +376,9 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
   {
   }
 
+  /**
+   * Creates a tooltip when the mouse is over a hidden columns marker
+   */
   @Override
   public void mouseMoved(MouseEvent evt)
   {
@@ -448,11 +403,11 @@ public class ScalePanel extends JPanel implements MouseMotionListener,
           ToolTipManager.sharedInstance().registerComponent(this);
           this.setToolTipText(MessageManager
                   .getString("label.reveal_hidden_columns"));
-          break;
+          repaint();
+          return;
         }
       }
     }
-    repaint();
   }
 
   /**
index a381e8b..83193dc 100755 (executable)
@@ -25,9 +25,9 @@ import jalview.jbgui.GSliderPanel;
 import jalview.schemes.ColourSchemeI;
 import jalview.util.MessageManager;
 
-import java.awt.event.ActionEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.beans.PropertyVetoException;
 import java.util.Iterator;
 
 import javax.swing.JInternalFrame;
@@ -91,6 +91,7 @@ public class SliderPanel extends GSliderPanel
 
     slider.addChangeListener(new ChangeListener()
     {
+      @Override
       public void stateChanged(ChangeEvent evt)
       {
         valueField.setText(slider.getValue() + "");
@@ -100,6 +101,7 @@ public class SliderPanel extends GSliderPanel
 
     slider.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mouseReleased(MouseEvent evt)
       {
         ap.paintAlignment(true);
@@ -158,6 +160,40 @@ public class SliderPanel extends GSliderPanel
   }
 
   /**
+   * Hides the PID slider panel if it is shown
+   */
+  public static void hidePIDSlider()
+  {
+    if (PIDSlider != null)
+    {
+      try
+      {
+        PIDSlider.setClosed(true);
+        PIDSlider = null;
+      } catch (PropertyVetoException ex)
+      {
+      }
+    }
+  }
+
+  /**
+   * Hides the conservation slider panel if it is shown
+   */
+  public static void hideConservationSlider()
+  {
+    if (conservationSlider != null)
+    {
+      try
+      {
+        conservationSlider.setClosed(true);
+        conservationSlider = null;
+      } catch (PropertyVetoException ex)
+      {
+      }
+    }
+  }
+
+  /**
    * DOCUMENT ME!
    */
   public static void showConservationSlider()
@@ -177,6 +213,7 @@ public class SliderPanel extends GSliderPanel
       conservationSlider
               .addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
               {
+                @Override
                 public void internalFrameClosed(
                         javax.swing.event.InternalFrameEvent e)
                 {
@@ -257,6 +294,7 @@ public class SliderPanel extends GSliderPanel
       PIDSlider
               .addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
               {
+                @Override
                 public void internalFrameClosed(
                         javax.swing.event.InternalFrameEvent e)
                 {
@@ -329,24 +367,6 @@ public class SliderPanel extends GSliderPanel
   /**
    * DOCUMENT ME!
    * 
-   * @param e
-   *          DOCUMENT ME!
-   */
-  public void valueField_actionPerformed(ActionEvent e)
-  {
-    try
-    {
-      int i = Integer.parseInt(valueField.getText());
-      slider.setValue(i);
-    } catch (NumberFormatException ex)
-    {
-      valueField.setText(slider.getValue() + "");
-    }
-  }
-
-  /**
-   * DOCUMENT ME!
-   * 
    * @param value
    *          DOCUMENT ME!
    */
@@ -365,6 +385,7 @@ public class SliderPanel extends GSliderPanel
     return Integer.parseInt(valueField.getText());
   }
 
+  @Override
   public void slider_mouseReleased(MouseEvent e)
   {
     if (ap.overviewPanel != null)
index a320a9c..c8aa94f 100755 (executable)
@@ -65,8 +65,6 @@ public class GAlignFrame extends JInternalFrame
 
   protected JMenuItem closeMenuItem = new JMenuItem();
 
-  protected JMenu colourMenu = new JMenu();
-
   protected JMenu webService = new JMenu();
 
   protected JMenuItem webServiceNoServices;
@@ -81,8 +79,6 @@ public class GAlignFrame extends JInternalFrame
 
   protected JMenu outputTextboxMenu = new JMenu();
 
-  protected JRadioButtonMenuItem textColour;
-
   protected JCheckBoxMenuItem annotationPanelMenuItem = new JCheckBoxMenuItem();
 
   protected JCheckBoxMenuItem colourTextMenuItem = new JCheckBoxMenuItem();
@@ -93,14 +89,10 @@ public class GAlignFrame extends JInternalFrame
 
   protected JMenuItem redoMenuItem = new JMenuItem();
 
-  protected JCheckBoxMenuItem conservationMenuItem;
-
   protected JCheckBoxMenuItem wrapMenuItem = new JCheckBoxMenuItem();
 
   protected JCheckBoxMenuItem renderGapsMenuItem = new JCheckBoxMenuItem();
 
-  protected JCheckBoxMenuItem abovePIDThreshold;
-
   public JCheckBoxMenuItem showSeqFeatures = new JCheckBoxMenuItem();
 
   JMenuItem copy = new JMenuItem();
@@ -109,8 +101,6 @@ public class GAlignFrame extends JInternalFrame
 
   JMenu pasteMenu = new JMenu();
 
-  protected JCheckBoxMenuItem applyToAllGroups;
-
   protected JCheckBoxMenuItem seqLimits = new JCheckBoxMenuItem();
 
   protected JCheckBoxMenuItem scaleAbove = new JCheckBoxMenuItem();
@@ -119,10 +109,20 @@ public class GAlignFrame extends JInternalFrame
 
   protected JCheckBoxMenuItem scaleRight = new JCheckBoxMenuItem();
 
-  protected JMenuItem modifyPID;
+  protected JCheckBoxMenuItem applyToAllGroups;
+
+  protected JMenu colourMenu = new JMenu();
+
+  protected JRadioButtonMenuItem textColour;
+
+  protected JCheckBoxMenuItem conservationMenuItem;
 
   protected JMenuItem modifyConservation;
 
+  protected JCheckBoxMenuItem abovePIDThreshold;
+
+  protected JMenuItem modifyPID;
+
   protected JMenuItem annotationColour;
 
   protected JMenu sortByTreeMenu = new JMenu();
@@ -752,6 +752,17 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, redoMenuItem, al);
 
+    conservationMenuItem.setText(MessageManager
+            .getString("action.by_conservation"));
+    conservationMenuItem.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        conservationMenuItem_actionPerformed();
+      }
+    });
+
     wrapMenuItem.setText(MessageManager.getString("label.wrap"));
     wrapMenuItem.addActionListener(new ActionListener()
     {
@@ -804,6 +815,17 @@ public class GAlignFrame extends JInternalFrame
     };
     addMenuActionAndAccelerator(keyStroke, findMenuItem, al);
 
+    abovePIDThreshold.setText(MessageManager
+            .getString("label.above_identity_threshold"));
+    abovePIDThreshold.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        abovePIDThreshold_actionPerformed();
+      }
+    });
+
     showSeqFeatures.setText(MessageManager
             .getString("label.show_sequence_features"));
     showSeqFeatures.addActionListener(new ActionListener()
@@ -1195,6 +1217,27 @@ public class GAlignFrame extends JInternalFrame
 
     });
 
+    modifyPID = new JMenuItem(
+            MessageManager.getString("label.modify_identity_threshold"));
+    modifyPID.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        modifyPID_actionPerformed();
+      }
+    });
+    modifyConservation.setText(MessageManager
+            .getString("label.modify_conservation_threshold"));
+    modifyConservation.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        modifyConservation_actionPerformed();
+      }
+    });
+
     sortByTreeMenu
             .setText(MessageManager.getString("action.by_tree_order"));
     sort.setText(MessageManager.getString("action.sort"));
index 1362007..0c79c6c 100755 (executable)
@@ -28,6 +28,9 @@ import java.awt.Dimension;
 import java.awt.FlowLayout;
 import java.awt.GridLayout;
 import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 
@@ -105,6 +108,7 @@ public class GSliderPanel extends JPanel
     slider.setDoubleBuffered(true);
     slider.addMouseListener(new MouseAdapter()
     {
+      @Override
       public void mouseReleased(MouseEvent e)
       {
         slider_mouseReleased(e);
@@ -115,11 +119,20 @@ public class GSliderPanel extends JPanel
     valueField.setPreferredSize(new Dimension(50, 12));
     valueField.setText("");
     valueField.setHorizontalAlignment(SwingConstants.CENTER);
-    valueField.addActionListener(new java.awt.event.ActionListener()
+    valueField.addActionListener(new ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
-        valueField_actionPerformed(e);
+        valueField_actionPerformed();
+      }
+    });
+    valueField.addFocusListener(new FocusAdapter()
+    {
+      @Override
+      public void focusLost(FocusEvent e)
+      {
+        valueField_actionPerformed();
       }
     });
     label.setFont(new java.awt.Font("Verdana", 0, 11));
@@ -134,6 +147,7 @@ public class GSliderPanel extends JPanel
     applyButton.setText(MessageManager.getString("action.apply"));
     applyButton.addActionListener(new java.awt.event.ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         applyButton_actionPerformed(e);
@@ -145,6 +159,7 @@ public class GSliderPanel extends JPanel
     undoButton.setText(MessageManager.getString("action.undo"));
     undoButton.addActionListener(new java.awt.event.ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         undoButton_actionPerformed(e);
@@ -157,6 +172,7 @@ public class GSliderPanel extends JPanel
             .getString("action.apply_all_groups"));
     allGroupsCheck.addActionListener(new java.awt.event.ActionListener()
     {
+      @Override
       public void actionPerformed(ActionEvent e)
       {
         allGroupsCheck_actionPerformed(e);
@@ -180,13 +196,18 @@ public class GSliderPanel extends JPanel
   }
 
   /**
-   * DOCUMENT ME!
-   * 
-   * @param e
-   *          DOCUMENT ME!
+   * Action on changing the slider text field value
    */
-  protected void valueField_actionPerformed(ActionEvent e)
+  protected void valueField_actionPerformed()
   {
+    try
+    {
+      int i = Integer.valueOf(valueField.getText());
+      slider.setValue(i);
+    } catch (NumberFormatException ex)
+    {
+      valueField.setText(String.valueOf(slider.getValue()));
+    }
   }
 
   /**
index f1b83b8..139ee19 100644 (file)
@@ -97,12 +97,12 @@ public class ParseHtmlBodyAndLinks
   public ParseHtmlBodyAndLinks(String description, boolean removeHTML,
           String newline)
   {
-    StringBuilder sb = new StringBuilder(description.length());
     if (description == null || description.length() == 0)
     {
       htmlContent = false;
       return;
     }
+    StringBuilder sb = new StringBuilder(description.length());
     if (description.toUpperCase().indexOf("<HTML>") == -1)
     {
       htmlContent = false;
index a9ad4c2..1d819c9 100644 (file)
@@ -205,12 +205,11 @@ public class ColumnSelectionTest
     assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
 
     // hiding column 4 expands [3, 3] to [3, 4]
-    // not fancy enough to coalesce this into [3, 5] though
+    // and merges to [5, 5] to make [3, 5]
     cs.hideColumns(4);
     hidden = cs.getHiddenColumns();
-    assertEquals(2, hidden.size());
-    assertEquals("[3, 4]", Arrays.toString(hidden.get(0)));
-    assertEquals("[5, 5]", Arrays.toString(hidden.get(1)));
+    assertEquals(1, hidden.size());
+    assertEquals("[3, 5]", Arrays.toString(hidden.get(0)));
 
     // clear hidden columns (note they are added to selected)
     cs.revealAllHiddenColumns();
@@ -761,4 +760,122 @@ public class ColumnSelectionTest
     assertEquals("[5, 7]", Arrays.toString(cs2.getHiddenColumns().get(0)));
     assertEquals("[10, 11]", Arrays.toString(cs2.getHiddenColumns().get(1)));
   }
+
+  /**
+   * Test for the case when a hidden range encloses more one already hidden
+   * range
+   */
+  @Test(groups = { "Functional" })
+  public void testHideColumns_subsumingHidden()
+  {
+    /*
+     * JAL-2370 bug scenario:
+     * two hidden ranges subsumed by a third
+     */
+    ColumnSelection cs = new ColumnSelection();
+    cs.hideColumns(49, 59);
+    cs.hideColumns(69, 79);
+    List<int[]> hidden = cs.getHiddenColumns();
+    assertEquals(2, hidden.size());
+    assertEquals("[49, 59]", Arrays.toString(hidden.get(0)));
+    assertEquals("[69, 79]", Arrays.toString(hidden.get(1)));
+  
+    cs.hideColumns(48, 80);
+    hidden = cs.getHiddenColumns();
+    assertEquals(1, hidden.size());
+    assertEquals("[48, 80]", Arrays.toString(hidden.get(0)));
+
+    /*
+     * another...joining hidden ranges
+     */
+    cs = new ColumnSelection();
+    cs.hideColumns(10, 20);
+    cs.hideColumns(30, 40);
+    cs.hideColumns(50, 60);
+    // hiding 21-49 should merge to one range
+    cs.hideColumns(21, 49);
+    hidden = cs.getHiddenColumns();
+    assertEquals(1, hidden.size());
+    assertEquals("[10, 60]", Arrays.toString(hidden.get(0)));
+
+    /*
+     * another...lef overlap, subsumption, right overlap,
+     * no overlap of existing hidden ranges
+     */
+    cs = new ColumnSelection();
+    cs.hideColumns(10, 20);
+    cs.hideColumns(10, 20);
+    cs.hideColumns(30, 35);
+    cs.hideColumns(40, 50);
+    cs.hideColumns(60, 70);
+
+    cs.hideColumns(15, 45);
+    hidden = cs.getHiddenColumns();
+    assertEquals(2, hidden.size());
+    assertEquals("[10, 50]", Arrays.toString(hidden.get(0)));
+    assertEquals("[60, 70]", Arrays.toString(hidden.get(1)));
+  }
+
+  @Test(groups = { "Functional" })
+  public void testStretchGroup_expand()
+  {
+    /*
+     * test that emulates clicking column 4 (selected)
+     * and dragging right to column 5 (all base 0)
+     */
+    ColumnSelection cs = new ColumnSelection();
+    cs.addElement(4);
+    SequenceGroup sg = new SequenceGroup();
+    sg.setStartRes(4);
+    sg.setEndRes(4);
+    cs.stretchGroup(5, sg, 4, 4);
+    assertEquals(cs.getSelected().size(), 2);
+    assertTrue(cs.contains(4));
+    assertTrue(cs.contains(5));
+    assertEquals(sg.getStartRes(), 4);
+    assertEquals(sg.getEndRes(), 5);
+
+    /*
+     * emulate drag right with columns 10-20 already selected
+     */
+    cs.clear();
+    for (int i = 10; i <= 20; i++)
+    {
+      cs.addElement(i);
+    }
+    assertEquals(cs.getSelected().size(), 11);
+    sg = new SequenceGroup();
+    sg.setStartRes(10);
+    sg.setEndRes(20);
+    cs.stretchGroup(21, sg, 10, 20);
+    assertEquals(cs.getSelected().size(), 12);
+    assertTrue(cs.contains(10));
+    assertTrue(cs.contains(21));
+    assertEquals(sg.getStartRes(), 10);
+    assertEquals(sg.getEndRes(), 21);
+  }
+
+  @Test(groups = { "Functional" })
+  public void testStretchGroup_shrink()
+  {
+    /*
+     * emulate drag left to 19 with columns 10-20 already selected
+     */
+    ColumnSelection cs = new ColumnSelection();
+    for (int i = 10; i <= 20; i++)
+    {
+      cs.addElement(i);
+    }
+    assertEquals(cs.getSelected().size(), 11);
+    SequenceGroup sg = new SequenceGroup();
+    sg.setStartRes(10);
+    sg.setEndRes(20);
+    cs.stretchGroup(19, sg, 10, 20);
+    assertEquals(cs.getSelected().size(), 10);
+    assertTrue(cs.contains(10));
+    assertTrue(cs.contains(19));
+    assertFalse(cs.contains(20));
+    assertEquals(sg.getStartRes(), 10);
+    assertEquals(sg.getEndRes(), 19);
+  }
 }