Merge branch 'develop' into efficiency/JAL-2034_JAL-1421
authorJim Procter <jprocter@issues.jalview.org>
Mon, 30 May 2016 12:20:53 +0000 (13:20 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Mon, 30 May 2016 12:20:53 +0000 (13:20 +0100)
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/OverviewPanel.java
src/jalview/appletgui/SeqPanel.java
src/jalview/datamodel/AlignmentView.java
src/jalview/datamodel/SequenceGroup.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/OverviewPanel.java
src/jalview/gui/SeqPanel.java
src/jalview/gui/SequenceRenderer.java

index e5f0053..386b2b2 100644 (file)
@@ -2232,7 +2232,10 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     }
     sg.setEndRes(viewport.getAlignment().getWidth() - 1);
     viewport.setSelectionGroup(sg);
-    alignPanel.paintAlignment(true);
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    alignPanel.paintAlignment(false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
     viewport.sendSelection();
   }
@@ -2249,7 +2252,10 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     viewport.setSelectionGroup(null);
     alignPanel.idPanel.idCanvas.searchResults = null;
     alignPanel.seqPanel.seqCanvas.highlightSearchResults(null);
-    alignPanel.paintAlignment(true);
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    alignPanel.paintAlignment(false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
     viewport.sendSelection();
   }
index bc64728..c068a4d 100755 (executable)
@@ -304,6 +304,10 @@ public class OverviewPanel extends Panel implements Runnable,
     AlignmentI alignment = av.getAlignment();
     for (row = 0; row <= sequencesHeight; row++)
     {
+      if (resizeAgain)
+      {
+        break;
+      }
       if ((int) (row * sampleRow) == lastrow)
       {
         sameRow++;
@@ -385,6 +389,10 @@ public class OverviewPanel extends Panel implements Runnable,
     {
       for (col = 0; col < width; col++)
       {
+        if (resizeAgain)
+        {
+          break;
+        }
         lastcol = (int) (col * sampleCol);
         {
           mg.translate(col, sequencesHeight);
index a4dad4b..9ec1ce4 100644 (file)
@@ -919,6 +919,21 @@ 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
+
+  /**
+   * set if av.getSelectionGroup() refers to a group that is defined on the
+   * alignment view, rather than a transient selection
+   */
+  private boolean editingDefinedGroup = false; // TODO: refactor to avcontroller
+                                               // or viewModel
+
   @Override
   public void mouseDragged(MouseEvent evt)
   {
@@ -1421,10 +1436,12 @@ public class SeqPanel extends Panel implements MouseMotionListener,
               && res < stretchGroup.getEndRes())
       {
         av.setSelectionGroup(stretchGroup);
+        editingDefinedGroup = true;
       }
       else
       {
         stretchGroup = null;
+        editingDefinedGroup = false;
       }
     }
 
@@ -1444,6 +1461,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
                   && allGroups[i].getEndRes() >= res)
           {
             stretchGroup = allGroups[i];
+            editingDefinedGroup = true;
             break;
           }
         }
@@ -1499,6 +1517,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       sg.setEndRes(res);
       sg.addSequence(sequence, false);
       av.setSelectionGroup(sg);
+      editingDefinedGroup = false;
       stretchGroup = sg;
 
       if (av.getConservationSelected())
@@ -1521,9 +1540,10 @@ 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);
+    needOverviewUpdate |= vischange && editingDefinedGroup;
     if (stretchGroup.cs != null)
     {
       stretchGroup.cs.alignmentChanged(stretchGroup,
@@ -1540,11 +1560,13 @@ public class SeqPanel extends Panel implements MouseMotionListener,
                 stretchGroup.getName());
       }
     }
+    PaintRefresher.Refresh(ap, av.getSequenceSetId());
+    ap.paintAlignment(needOverviewUpdate);
+    needOverviewUpdate =false;
+    editingDefinedGroup = false;
     changeEndRes = false;
     changeStartRes = false;
     stretchGroup = null;
-    PaintRefresher.Refresh(ap, av.getSequenceSetId());
-    ap.paintAlignment(true);
     av.sendSelection();
   }
 
@@ -1596,6 +1618,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       if (res > (stretchGroup.getStartRes() - 1))
       {
         stretchGroup.setEndRes(res);
+        needOverviewUpdate |= editingDefinedGroup;
       }
     }
     else if (changeStartRes)
@@ -1603,6 +1626,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       if (res < (stretchGroup.getEndRes() + 1))
       {
         stretchGroup.setStartRes(res);
+        needOverviewUpdate |= editingDefinedGroup;
       }
     }
 
@@ -1636,6 +1660,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
       if (stretchGroup.getSequences(null).contains(nextSeq))
       {
         stretchGroup.deleteSequence(seq, false);
+        needOverviewUpdate |= editingDefinedGroup;
       }
       else
       {
@@ -1645,6 +1670,7 @@ public class SeqPanel extends Panel implements MouseMotionListener,
         }
 
         stretchGroup.addSequence(nextSeq, false);
+        needOverviewUpdate |= editingDefinedGroup;
       }
     }
 
index 756a116..fa72e80 100644 (file)
@@ -26,7 +26,6 @@ import jalview.util.ShiftList;
 import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Vector;
 
 /**
  * Transient object compactly representing a 'view' of an alignment - with
@@ -69,13 +68,51 @@ public class AlignmentView
    */
   private class ScGroup
   {
-    public Vector seqs;
+    public List<SeqCigar> seqs;
 
     public SequenceGroup sg;
 
     ScGroup()
     {
-      seqs = new Vector();
+      seqs = new ArrayList<SeqCigar>();
+    }
+
+    /**
+     * @param seq
+     * @return true if seq was not a member before and was added to group
+     */
+    public boolean add(SeqCigar seq)
+    {
+      if (!seq.isMemberOf(this))
+      {
+        seqs.add(seq);
+        seq.setGroupMembership(this);
+        return true;
+      }
+      else
+      {
+        return false;
+      }
+    }
+
+    /**
+     * 
+     * @param seq
+     * @return true if seq was a member and was removed from group
+     */
+    public boolean remove(SeqCigar seq)
+    {
+      if (seq.removeGroupMembership(this))
+      {
+        seqs.remove(seq);
+        return true;
+      }
+      return false;
+    }
+
+    public int size()
+    {
+      return seqs.size();
     }
   }
 
@@ -83,7 +120,7 @@ public class AlignmentView
    * vector of selected seqCigars. This vector is also referenced by each
    * seqCigar contained in it.
    */
-  private Vector selected;
+  private ScGroup selected;
 
   /**
    * Construct an alignmentView from a live jalview alignment view. Note -
@@ -124,7 +161,7 @@ public class AlignmentView
     if (selection != null && selection.getSize() > 0)
     {
       List<SequenceI> sel = selection.getSequences(null);
-      this.selected = new Vector();
+      this.selected = new ScGroup();
       selseqs = selection
               .getSequencesInOrder(alignment, selectedRegionOnly);
     }
@@ -194,8 +231,7 @@ public class AlignmentView
         if (selection != null && selection.getSize() > 0
                 && !selectedRegionOnly)
         {
-          sequences[csi].setGroupMembership(selected);
-          selected.addElement(sequences[csi]);
+          selected.add(sequences[csi]);
         }
         if (seqsets != null)
         {
@@ -203,9 +239,8 @@ public class AlignmentView
           {
             if ((seqsets.get(sg)).contains(selseqs[i]))
             {
-              sequences[csi].setGroupMembership(sgrps[sg]);
               sgrps[sg].sg.deleteSequence(selseqs[i], false);
-              sgrps[sg].seqs.addElement(sequences[csi]);
+              sgrps[sg].add(sequences[csi]);
               if (!addedgps[sg])
               {
                 if (scGroups == null)
@@ -1073,10 +1108,10 @@ public class AlignmentView
                 + sgr.sg.getEndRes());
         for (int s = 0; s < sgr.seqs.size(); s++)
         {
-          if (!((SeqCigar) sgr.seqs.elementAt(s)).isMemberOf(sgr))
+          // JBPnote this should be a unit test for ScGroup
+          if (!sgr.seqs.get(s).isMemberOf(sgr))
           {
-            os.println("** WARNING: sequence "
-                    + ((SeqCigar) sgr.seqs.elementAt(s)).toString()
+            os.println("** WARNING: sequence " + sgr.seqs.get(s).toString()
                     + " is not marked as member of group.");
           }
         }
index 0e8fa17..ee4d7c2 100755 (executable)
@@ -504,14 +504,31 @@ public class SequenceGroup implements AnnotatedCollectionI
   }
 
   /**
-   * calculate residue conservation for group - but only if necessary.
+   * calculate residue conservation and colourschemes for group - but only if
+   * necessary. returns true if the calculation resulted in a visible change to
+   * group
    */
-  public void recalcConservation()
+  public boolean recalcConservation()
+  {
+    return recalcConservation(false);
+  }
+
+  /**
+   * calculate residue conservation for group - but only if necessary. returns
+   * true if the calculation resulted in a visible change to group
+   * 
+   * @param defer
+   *          when set, colourschemes for this group are not refreshed after
+   *          recalculation
+   */
+  public boolean recalcConservation(boolean defer)
   {
     if (cs == null && consensus == null && conservation == null)
     {
-      return;
+      return false;
     }
+    // TODO: try harder to detect changes in state in order to minimise
+    // recalculation effort
     try
     {
       Hashtable cnsns[] = AAFrequency.calculate(sequences, startRes,
@@ -545,16 +562,22 @@ public class SequenceGroup implements AnnotatedCollectionI
           }
         }
       }
-      if (cs != null)
+      if (cs != null && !defer)
       {
+        // TODO: JAL-2034 should cs.alignmentChanged modify return state
         cs.alignmentChanged(context != null ? context : this, null);
+        return true;
+      }
+      else
+      {
+        return false;
       }
     } catch (java.lang.OutOfMemoryError err)
     {
       // TODO: catch OOM
       System.out.println("Out of memory loading groups: " + err);
     }
-
+    return false;
   }
 
   private void _updateConservationRow(Conservation c)
@@ -894,6 +917,7 @@ public class SequenceGroup implements AnnotatedCollectionI
   /**
    * @return the representative sequence for this group
    */
+  @Override
   public SequenceI getSeqrep()
   {
     return seqrep;
@@ -906,6 +930,7 @@ public class SequenceGroup implements AnnotatedCollectionI
    * @param seqrep
    *          the seqrep to set (null means no sequence representative)
    */
+  @Override
   public void setSeqrep(SequenceI seqrep)
   {
     this.seqrep = seqrep;
@@ -915,6 +940,7 @@ public class SequenceGroup implements AnnotatedCollectionI
    * 
    * @return true if group has a sequence representative
    */
+  @Override
   public boolean hasSeqrep()
   {
     return seqrep != null;
index d9d5f27..08122a4 100644 (file)
@@ -2433,7 +2433,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     sg.setEndRes(viewport.getAlignment().getWidth() - 1);
     viewport.setSelectionGroup(sg);
     viewport.sendSelection();
-    alignPanel.paintAlignment(true);
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    alignPanel.paintAlignment(false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
   }
 
@@ -2456,7 +2459,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     viewport.setSelectionGroup(null);
     alignPanel.getSeqPanel().seqCanvas.highlightSearchResults(null);
     alignPanel.getIdPanel().getIdCanvas().searchResults = null;
-    alignPanel.paintAlignment(true);
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
+    alignPanel.paintAlignment(false);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
     viewport.sendSelection();
   }
@@ -2483,6 +2489,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       sg.addOrRemove(viewport.getAlignment().getSequenceAt(i), false);
     }
+    // JAL-2034 - should delegate to
+    // alignPanel to decide if overview needs
+    // updating.
 
     alignPanel.paintAlignment(true);
     PaintRefresher.Refresh(alignPanel, viewport.getSequenceSetId());
index de0dbe5..c2a401d 100755 (executable)
@@ -295,14 +295,25 @@ public class OverviewPanel extends JPanel implements Runnable
     final boolean hasHiddenRows = av.hasHiddenRows(), hasHiddenCols = av
             .hasHiddenColumns();
     boolean hiddenRow = false;
+    // get hidden row and hidden column map once at beginning.
+    // clone featureRenderer settings to avoid race conditions... if state is
+    // updated just need to refresh again
     for (row = 0; row < sequencesHeight; row++)
     {
+      if (resizeAgain)
+      {
+        break;
+      }
       if ((int) (row * sampleRow) == lastrow)
       {
         // No need to recalculate the colours,
         // Just copy from the row above
         for (col = 0; col < width; col++)
         {
+          if (resizeAgain)
+          {
+            break;
+          }
           miniMe.setRGB(col, row, miniMe.getRGB(col, row - 1));
         }
         continue;
@@ -340,6 +351,10 @@ public class OverviewPanel extends JPanel implements Runnable
 
       for (col = 0; col < width; col++)
       {
+        if (resizeAgain)
+        {
+          break;
+        }
         if ((int) (col * sampleCol) == lastcol
                 && (int) (row * sampleRow) == lastrow)
         {
@@ -380,6 +395,10 @@ public class OverviewPanel extends JPanel implements Runnable
       renderer.updateFromAlignViewport(av);
       for (col = 0; col < width; col++)
       {
+        if (resizeAgain)
+        {
+          break;
+        }
         lastcol = (int) (col * sampleCol);
         {
           mg.translate(col, sequencesHeight);
index bd4b234..9006262 100644 (file)
@@ -825,6 +825,20 @@ public class SeqPanel extends JPanel implements MouseListener,
   String lastTooltip;
 
   /**
+   * 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 = false; // TODO: refactor to avcontroller
+
+  /**
+   * set if av.getSelectionGroup() refers to a group that is defined on the
+   * alignment view, rather than a transient selection
+   */
+  private boolean editingDefinedGroup = false;  // TODO: refactor to avcontroller or viewModel
+
+  /**
    * Set status message in alignment panel
    * 
    * @param sequence
@@ -1568,10 +1582,12 @@ public class SeqPanel extends JPanel implements MouseListener,
               && (res < stretchGroup.getEndRes()))
       {
         av.setSelectionGroup(stretchGroup);
+        editingDefinedGroup = true;
       }
       else
       {
         stretchGroup = null;
+        editingDefinedGroup = false;
       }
     }
     else if (!stretchGroup.getSequences(null).contains(sequence)
@@ -1590,6 +1606,7 @@ public class SeqPanel extends JPanel implements MouseListener,
                   && (allGroups[i].getEndRes() >= res))
           {
             stretchGroup = allGroups[i];
+            editingDefinedGroup = true;
             break;
           }
         }
@@ -1639,7 +1656,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       sg.setEndRes(res);
       sg.addSequence(sequence, false);
       av.setSelectionGroup(sg);
-
+      editingDefinedGroup = false;
       stretchGroup = sg;
 
       if (av.getConservationSelected())
@@ -1682,9 +1699,10 @@ public class SeqPanel extends JPanel implements MouseListener,
     {
       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);
+    needOverviewUpdate |= vischange && editingDefinedGroup;
     if (stretchGroup.cs != null)
     {
       stretchGroup.cs.alignmentChanged(stretchGroup,
@@ -1702,8 +1720,9 @@ public class SeqPanel extends JPanel implements MouseListener,
       }
     }
     PaintRefresher.Refresh(this, av.getSequenceSetId());
-    ap.paintAlignment(true);
-
+    ap.paintAlignment(needOverviewUpdate);
+    needOverviewUpdate =false;
+    editingDefinedGroup = false;
     changeEndRes = false;
     changeStartRes = false;
     stretchGroup = null;
@@ -1757,6 +1776,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       if (res > (stretchGroup.getStartRes() - 1))
       {
         stretchGroup.setEndRes(res);
+        needOverviewUpdate |= editingDefinedGroup;
       }
     }
     else if (changeStartRes)
@@ -1764,6 +1784,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       if (res < (stretchGroup.getEndRes() + 1))
       {
         stretchGroup.setStartRes(res);
+        needOverviewUpdate |= editingDefinedGroup;
       }
     }
 
@@ -1797,6 +1818,7 @@ public class SeqPanel extends JPanel implements MouseListener,
       if (stretchGroup.getSequences(null).contains(nextSeq))
       {
         stretchGroup.deleteSequence(seq, false);
+        needOverviewUpdate |= editingDefinedGroup;
       }
       else
       {
@@ -1806,6 +1828,7 @@ public class SeqPanel extends JPanel implements MouseListener,
         }
 
         stretchGroup.addSequence(nextSeq, false);
+        needOverviewUpdate |= editingDefinedGroup;
       }
     }
 
index 258a229..17d69f4 100755 (executable)
@@ -91,6 +91,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
   @Override
   public Color getResidueBoxColour(SequenceI seq, int i)
   {
+    // rate limiting step when rendering overview for lots of groups
     allGroups = av.getAlignment().findAllGroups(seq);
 
     if (inCurrentSequenceGroup(i))