JAL-845 further code/tests/refactoring
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 6 Feb 2015 14:12:19 +0000 (14:12 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Fri, 6 Feb 2015 14:12:19 +0000 (14:12 +0000)
17 files changed:
resources/lang/Messages.properties
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentI.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/Desktop.java
src/jalview/gui/Jalview2XML.java
src/jalview/gui/JvSwingUtils.java
src/jalview/gui/PaintRefresher.java
src/jalview/gui/SplitFrame.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/util/StringUtils.java
test/jalview/datamodel/AlignmentTest.java
test/jalview/gui/JvSwingUtilsTest.java [new file with mode: 0644]
test/jalview/gui/PaintRefresherTest.java [new file with mode: 0644]
test/jalview/util/StringUtilsTest.java

index 0d67dac..de4ceb5 100644 (file)
@@ -380,6 +380,7 @@ label.automatically_associate_pdb_files_with_sequences_same_name = Do you want t
 label.automatically_associate_pdb_files_by_name = Automatically Associate PDB files by name
 label.ignore_unmatched_dropped_files_info = <html>Do you want to <em>ignore</em> the {0} files whose names did not match any sequence IDs ?</html>
 label.ignore_unmatched_dropped_files = Ignore unmatched dropped files?
+label.view_name_original = Original
 label.enter_view_name = Enter View Name
 label.enter_label = Enter label
 label.enter_label_for_the_structure = Enter a label for the structure?
@@ -699,6 +700,7 @@ label.align_cdna_tip = Any linked cDNA sequences will be realigned to match this
 label.cdna_aligned = {0} sequences in {1} alignments were realigned
 label.view_as_cdna = Show aligned cDNA
 label.view_as_cdna_tip = Open a new alignment of the related cDNA sequences
+label.linked_view_title = {0} and {1}
 label.align = Align
 label.extract_scores = Extract Scores
 label.get_cross_refs = Get Cross References
index f660697..5b41c1d 100755 (executable)
@@ -67,6 +67,8 @@ public class Alignment implements AlignmentI
 
   public Hashtable alignmentProperties;
 
+  private Set<AlignedCodonFrame> codonFrameList = new LinkedHashSet<AlignedCodonFrame>();
+
   private void initAlignment(SequenceI[] seqs)
   {
     int i = 0;
@@ -101,6 +103,13 @@ public class Alignment implements AlignmentI
     {
       seqs[i] = new Sequence(seqs[i]);
     }
+
+    /*
+     * Share the same dataset sequence mappings (if any). TODO: find a better
+     * place for these to live (alignment dataset?).
+     */
+    this.codonFrameList = ((Alignment) al).codonFrameList;
+
     initAlignment(seqs);
   }
 
@@ -142,11 +151,6 @@ public class Alignment implements AlignmentI
     // this(compactAlignment.refCigars);
   }
 
-  /**
-   * DOCUMENT ME!
-   * 
-   * @return DOCUMENT ME!
-   */
   @Override
   public List<SequenceI> getSequences()
   {
@@ -749,6 +753,28 @@ public class Alignment implements AlignmentI
     return true;
   }
 
+  /**
+   * Delete all annotations, including auto-calculated if the flag is set true.
+   * Returns true if at least one annotation was deleted, else false.
+   * 
+   * @param includingAutoCalculated
+   * @return
+   */
+  @Override
+  public boolean deleteAllAnnotations(boolean includingAutoCalculated)
+  {
+    boolean result = false;
+    for (AlignmentAnnotation alan : getAlignmentAnnotation())
+    {
+      if (!alan.autoCalculated || includingAutoCalculated)
+      {
+        deleteAnnotation(alan);
+        result = true;
+      }
+    }
+    return result;
+  }
+
   /*
    * (non-Javadoc)
    * 
@@ -1234,10 +1260,6 @@ public class Alignment implements AlignmentI
     return alignmentProperties;
   }
 
-  // AlignedCodonFrame[] codonFrameList = null;
-
-  LinkedHashSet<AlignedCodonFrame> codonFrameList = new LinkedHashSet<AlignedCodonFrame>();
-
   /*
    * (non-Javadoc)
    * 
@@ -1278,8 +1300,20 @@ public class Alignment implements AlignmentI
     return cframes;
   }
 
-  /*
-   * (non-Javadoc)
+  /**
+   * Sets the codon frame mappings (replacing any existing mappings).
+   * 
+   * @see jalview.datamodel.AlignmentI#setCodonFrames()
+   */
+  @Override
+  public void setCodonFrames(Set<AlignedCodonFrame> acfs)
+  {
+    this.codonFrameList = acfs;
+  }
+
+  /**
+   * Returns the set of codon frame mappings. Any changes to the returned set
+   * will affect the alignment.
    * 
    * @see jalview.datamodel.AlignmentI#getCodonFrames()
    */
index ac2596a..fe93683 100755 (executable)
@@ -219,6 +219,15 @@ public interface AlignmentI extends AnnotatedCollectionI
   void setAnnotationIndex(AlignmentAnnotation aa, int index);
 
   /**
+   * Delete all annotations, including auto-calculated if the flag is set true.
+   * Returns true if at least one annotation was deleted, else false.
+   * 
+   * @param includingAutoCalculated
+   * @return
+   */
+  boolean deleteAllAnnotations(boolean includingAutoCalculated);
+
+  /**
    * Deletes a specific AlignmentAnnotation from the alignment, and removes its
    * reference from any SequenceI or SequenceGroup object's annotation if and
    * only if aa is contained within the alignment's annotation vector.
@@ -369,6 +378,11 @@ public interface AlignmentI extends AnnotatedCollectionI
   Set<AlignedCodonFrame> getCodonFrames();
 
   /**
+   * Set the codon frame mappings (replacing any existing set).
+   */
+  void setCodonFrames(Set<AlignedCodonFrame> acfs);
+
+  /**
    * get codon frames involving sequenceI
    */
   List<AlignedCodonFrame> getCodonFrame(SequenceI seq);
index 14a5de3..47f1c2a 100644 (file)
@@ -152,12 +152,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
         IProgressIndicator, AlignViewControllerGuiI
 {
 
-  /** DOCUMENT ME!! */
   public static final int DEFAULT_WIDTH = 700;
 
-  /** DOCUMENT ME!! */
   public static final int DEFAULT_HEIGHT = 500;
 
+  /*
+   * The currently displayed panel (selected tabbed view if more than one)
+   */
   public AlignmentPanel alignPanel;
 
   AlignViewport viewport;
@@ -1663,16 +1664,16 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       EditCommand editCommand = (EditCommand) command;
       al = editCommand.getAlignment();
-      Vector comps = (Vector) PaintRefresher.components.get(viewport
+      List<Component> comps = PaintRefresher.components.get(viewport
               .getSequenceSetId());
 
-      for (int i = 0; i < comps.size(); i++)
+      for (Component comp : comps)
       {
-        if (comps.elementAt(i) instanceof AlignmentPanel)
+        if (comp instanceof AlignmentPanel)
         {
-          if (al == ((AlignmentPanel) comps.elementAt(i)).av.getAlignment())
+          if (al == ((AlignmentPanel) comp).av.getAlignment())
           {
-            originalSource = ((AlignmentPanel) comps.elementAt(i)).av;
+            originalSource = ((AlignmentPanel) comp).av;
             break;
           }
         }
@@ -2705,71 +2706,99 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void newView_actionPerformed(ActionEvent e)
   {
-    newView(true);
-  }
+    /*
+     * Note if the current view has a protein/cdna complementary view
+     */
+    AlignViewportI linkedView = this.viewport.getCodingComplement();
 
-  /**
-   * 
-   * @param copyAnnotation
-   *          if true then duplicate all annnotation, groups and settings
-   * @return new alignment panel, already displayed.
-   */
-  public AlignmentPanel newView(boolean copyAnnotation)
-  {
-    return newView(null, copyAnnotation);
-  }
+    AlignmentPanel newPanel = newView(null, true);
 
-  /**
-   * 
-   * @param viewTitle
-   *          title of newly created view
-   * @return new alignment panel, already displayed.
-   */
-  public AlignmentPanel newView(String viewTitle)
-  {
-    return newView(viewTitle, true);
+    /*
+     * If the original view has a protein/cdna linked view, make and link a new
+     * view there also.
+     */
+    // TODO refactor the hell out of this - move to a controller, lose the casts
+    // and direct member access, etc
+    if (linkedView != null)
+    {
+      AlignFrame linkedAlignFrame = ((AlignViewport) linkedView)
+              .getAlignPanel().alignFrame;
+      AlignmentPanel newLinkedPanel = linkedAlignFrame.newView(null, true);
+      newLinkedPanel.av.viewName = newPanel.av.viewName;
+      newPanel.av.setCodingComplement(newLinkedPanel.av);
+      final StructureSelectionManager ssm = StructureSelectionManager
+              .getStructureSelectionManager(Desktop.instance);
+      ssm.addCommandListener(newPanel.av);
+      ssm.addCommandListener(newLinkedPanel.av);
+
+    }
   }
 
   /**
+   * Creates and shows a new view of the current alignment.
    * 
    * @param viewTitle
-   *          title of newly created view
+   *          title of newly created view; if null, one will be generated
    * @param copyAnnotation
    *          if true then duplicate all annnotation, groups and settings
    * @return new alignment panel, already displayed.
    */
   public AlignmentPanel newView(String viewTitle, boolean copyAnnotation)
   {
+    /*
+     * Create a new AlignmentPanel (with its own, new Viewport)
+     */
     AlignmentPanel newap = new Jalview2XML().copyAlignPanel(alignPanel,
             true);
     if (!copyAnnotation)
     {
-      // just remove all the current annotation except for the automatic stuff
+      /*
+       * remove all groups and annotation except for the automatic stuff
+       */
       newap.av.getAlignment().deleteAllGroups();
-      for (AlignmentAnnotation alan : newap.av.getAlignment()
-              .getAlignmentAnnotation())
-      {
-        if (!alan.autoCalculated)
-        {
-          newap.av.getAlignment().deleteAnnotation(alan);
-        }
-        ;
-      }
+      newap.av.getAlignment().deleteAllAnnotations(false);
     }
 
     newap.av.gatherViewsHere = false;
 
     if (viewport.viewName == null)
     {
-      viewport.viewName = "Original";
+      viewport.viewName = MessageManager
+              .getString("label.view_name_original");
     }
 
+    /*
+     * Views share the same edits, undo and redo stacks, mappings.
+     */
     newap.av.setHistoryList(viewport.getHistoryList());
     newap.av.setRedoList(viewport.getRedoList());
+    newap.av.getAlignment().setCodonFrames(
+            viewport.getAlignment().getCodonFrames());
+
+    newap.av.viewName = getNewViewName(viewTitle);
+
+    addAlignmentPanel(newap, true);
+    newap.alignmentChanged();
+
+    if (alignPanels.size() == 2)
+    {
+      viewport.gatherViewsHere = true;
+    }
+    tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
+    return newap;
+  }
 
+  /**
+   * Make a new name for the view, ensuring it is unique within the current
+   * sequenceSetId. (This used to be essential for Jalview Project archives, but
+   * these now use viewId. Unique view names are still desirable for usability.)
+   * 
+   * @param viewTitle
+   * @return
+   */
+  protected String getNewViewName(String viewTitle)
+  {
     int index = Desktop.getViewCount(viewport.getSequenceSetId());
-    // make sure the new view has a unique name - this is essential for Jalview
-    // 2 archives
     boolean addFirstIndex = false;
     if (viewTitle == null || viewTitle.trim().length() == 0)
     {
@@ -2781,45 +2810,55 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
       index = 1;// we count from 1 if given a specific name
     }
     String newViewName = viewTitle + ((addFirstIndex) ? " " + index : "");
-    Vector comps = (Vector) PaintRefresher.components.get(viewport
+
+    List<Component> comps = PaintRefresher.components.get(viewport
             .getSequenceSetId());
-    Vector existingNames = new Vector();
-    for (int i = 0; i < comps.size(); i++)
-    {
-      if (comps.elementAt(i) instanceof AlignmentPanel)
-      {
-        AlignmentPanel ap = (AlignmentPanel) comps.elementAt(i);
-        if (!existingNames.contains(ap.av.viewName))
-        {
-          existingNames.addElement(ap.av.viewName);
-        }
-      }
-    }
+
+    List<String> existingNames = getExistingViewNames(comps);
 
     while (existingNames.contains(newViewName))
     {
       newViewName = viewTitle + " " + (++index);
     }
+    return newViewName;
+  }
 
-    newap.av.viewName = newViewName;
-
-    addAlignmentPanel(newap, true);
-    newap.alignmentChanged();
-
-    if (alignPanels.size() == 2)
+  /**
+   * Returns a list of distinct view names found in the given list of
+   * components. View names are held on the viewport of an AlignmentPanel.
+   * 
+   * @param comps
+   * @return
+   */
+  protected List<String> getExistingViewNames(List<Component> comps)
+  {
+    List<String> existingNames = new ArrayList<String>();
+    for (Component comp : comps)
     {
-      viewport.gatherViewsHere = true;
+      if (comp instanceof AlignmentPanel)
+      {
+        AlignmentPanel ap = (AlignmentPanel) comp;
+        if (!existingNames.contains(ap.av.viewName))
+        {
+          existingNames.add(ap.av.viewName);
+        }
+      }
     }
-    tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1);
-    return newap;
+    return existingNames;
   }
 
+  /**
+   * Explode tabbed views into separate windows.
+   */
   @Override
   public void expandViews_actionPerformed(ActionEvent e)
   {
     Desktop.instance.explodeViews(this);
   }
 
+  /**
+   * Gather views in separate windows back into a tabbed presentation.
+   */
   @Override
   public void gatherViews_actionPerformed(ActionEvent e)
   {
@@ -3657,8 +3696,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   {
 
     Component[] menuItems = colourMenu.getMenuComponents();
-    int i, iSize = menuItems.length;
-    for (i = 0; i < iSize; i++)
+    int iSize = menuItems.length;
+    for (int i = 0; i < iSize; i++)
     {
       if (menuItems[i].getName() != null
               && menuItems[i].getName().equals("USER_DEFINED"))
@@ -3919,7 +3958,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void averageDistanceTreeMenuItem_actionPerformed(ActionEvent e)
   {
-    NewTreePanel("AV", "PID", "Average distance tree using PID");
+    newTreePanel("AV", "PID", "Average distance tree using PID");
   }
 
   /**
@@ -3931,7 +3970,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void neighbourTreeMenuItem_actionPerformed(ActionEvent e)
   {
-    NewTreePanel("NJ", "PID", "Neighbour joining tree using PID");
+    newTreePanel("NJ", "PID", "Neighbour joining tree using PID");
   }
 
   /**
@@ -3943,7 +3982,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void njTreeBlosumMenuItem_actionPerformed(ActionEvent e)
   {
-    NewTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
+    newTreePanel("NJ", "BL", "Neighbour joining tree using BLOSUM62");
   }
 
   /**
@@ -3955,7 +3994,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   protected void avTreeBlosumMenuItem_actionPerformed(ActionEvent e)
   {
-    NewTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
+    newTreePanel("AV", "BL", "Average distance tree using BLOSUM62");
   }
 
   /**
@@ -3968,7 +4007,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
    * @param title
    *          DOCUMENT ME!
    */
-  void NewTreePanel(String type, String pwType, String title)
+  void newTreePanel(String type, String pwType, String title)
   {
     TreePanel tp;
 
@@ -4183,7 +4222,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     {
       String treecalcnm = MessageManager.getString("label.tree_calc_"
               + type.toLowerCase());
-      for (final Object pwtype : ResidueProperties.scoreMatrices.keySet())
+      for (final String pwtype : ResidueProperties.scoreMatrices.keySet())
       {
         JMenuItem tm = new JMenuItem();
         ScoreModelI sm = ResidueProperties.scoreMatrices.get(pwtype);
@@ -4199,7 +4238,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
             @Override
             public void actionPerformed(ActionEvent e)
             {
-              NewTreePanel(type, (String) pwtype, title);
+              newTreePanel(type, pwtype, title);
             }
           });
           calculateTree.add(tm);
@@ -4209,21 +4248,18 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
     sortByTreeMenu.removeAll();
 
-    Vector comps = (Vector) PaintRefresher.components.get(viewport
+    List<Component> comps = PaintRefresher.components.get(viewport
             .getSequenceSetId());
-    Vector treePanels = new Vector();
-    int i, iSize = comps.size();
-    for (i = 0; i < iSize; i++)
+    List<TreePanel> treePanels = new ArrayList<TreePanel>();
+    for (Component comp : comps)
     {
-      if (comps.elementAt(i) instanceof TreePanel)
+      if (comp instanceof TreePanel)
       {
-        treePanels.add(comps.elementAt(i));
+        treePanels.add((TreePanel) comp);
       }
     }
 
-    iSize = treePanels.size();
-
-    if (iSize < 1)
+    if (treePanels.size() < 1)
     {
       sortByTreeMenu.setVisible(false);
       return;
@@ -4231,9 +4267,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
 
     sortByTreeMenu.setVisible(true);
 
-    for (i = 0; i < treePanels.size(); i++)
+    for (final TreePanel tp : treePanels)
     {
-      final TreePanel tp = (TreePanel) treePanels.elementAt(i);
       final JMenuItem item = new JMenuItem(tp.getTitle());
       item.addActionListener(new java.awt.event.ActionListener()
       {
@@ -5330,6 +5365,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
+  /**
+   * Method invoked by the ChangeListener on the tabbed pane, in other words
+   * when a different tabbed pane is selected by the user or programmatically.
+   */
   @Override
   public void tabSelectionChanged(int index)
   {
@@ -5342,19 +5381,22 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
     }
   }
 
+  /**
+   * On right mouse click on view tab, prompt for and set new view name.
+   */
   @Override
   public void tabbedPane_mousePressed(MouseEvent e)
   {
     if (SwingUtilities.isRightMouseButton(e))
     {
-      String reply = JOptionPane.showInternalInputDialog(this,
-              MessageManager.getString("label.enter_view_name"),
-              MessageManager.getString("label.enter_view_name"),
+      String msg = MessageManager.getString("label.enter_view_name");
+      String reply = JOptionPane.showInternalInputDialog(this, msg, msg,
               JOptionPane.QUESTION_MESSAGE);
 
       if (reply != null)
       {
         viewport.viewName = reply;
+        // TODO warn if reply is in getExistingViewNames()?
         tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), reply);
       }
     }
index 5a9fa6b..676d180 100644 (file)
@@ -59,6 +59,7 @@ import jalview.structure.SelectionSource;
 import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
 import jalview.util.MessageManager;
+import jalview.util.StringUtils;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.params.AutoCalcSetting;
 
@@ -1146,7 +1147,6 @@ public class AlignViewport extends AlignmentViewport implements
   {
     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(this
             .getSequenceSetId());
-    AlignmentPanel ap = null;
     for (int p = 0; aps != null && p < aps.length; p++)
     {
       if (aps[p].av == this)
@@ -1498,6 +1498,7 @@ public class AlignViewport extends AlignmentViewport implements
      */
     AlignFrame newAlignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH,
             AlignFrame.DEFAULT_HEIGHT);
+    newAlignFrame.setTitle(title);
 
     /*
      * Identify protein and dna alignments. Make a copy of this one if opening
@@ -1570,27 +1571,35 @@ public class AlignViewport extends AlignmentViewport implements
        */
       AlignFrame copyMe = new AlignFrame(thisAlignment,
               AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
-      copyMe.setTitle(""); // TODO would like this AlignFrame.title here
+      copyMe.setTitle(getAlignPanel().alignFrame.getTitle());
       final AlignFrame proteinFrame = al.isNucleotide() ? copyMe
               : newAlignFrame;
       final AlignFrame cdnaFrame = al.isNucleotide() ? newAlignFrame
               : copyMe;
-      newAlignFrame.setTitle(title);
 
       cdnaFrame.setVisible(true);
       proteinFrame.setVisible(true);
+      String proteinShortName = StringUtils.getLastToken(
+              proteinFrame.getTitle(), "/");
+      String dnaShortName = StringUtils.getLastToken(cdnaFrame.getTitle(),
+              "/");
+      String linkedTitle = MessageManager.formatMessage(
+              "label.linked_view_title", dnaShortName, proteinShortName);
       JInternalFrame splitFrame = new SplitFrame(cdnaFrame, proteinFrame);
-      Desktop.addInternalFrame(splitFrame, title, AlignFrame.DEFAULT_WIDTH,
+      Desktop.addInternalFrame(splitFrame, linkedTitle,
+              AlignFrame.DEFAULT_WIDTH,
               AlignFrame.DEFAULT_HEIGHT);
 
       /*
-       * Set the frames to list for each other's edit and sort commands.
+       * Set the frames to listen for each other's edit and sort commands.
        */
       ssm.addCommandListener(cdnaFrame.getViewport());
       ssm.addCommandListener(proteinFrame.getViewport());
 
       /*
-       * cDNA view will mirror edits, selection, sorting, show/hide on protein
+       * 'Coding complement' (dna/protein) views will mirror each others' edits,
+       * selections, sorting etc as decided from time to time by the relevant
+       * authorities.
        */
       proteinFrame.getViewport().setCodingComplement(cdnaFrame.getViewport());
     }
index fc36ebb..4bf350b 100644 (file)
@@ -92,9 +92,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
    * Creates a new AlignmentPanel object.
    * 
    * @param af
-   *          DOCUMENT ME!
    * @param av
-   *          DOCUMENT ME!
    */
   public AlignmentPanel(AlignFrame af, final AlignViewport av)
   {
@@ -650,7 +648,6 @@ public class AlignmentPanel extends GAlignmentPanel implements
    */
   public void adjustmentValueChanged(AdjustmentEvent evt)
   {
-
     int oldX = av.getStartRes();
     int oldY = av.getStartSeq();
 
@@ -1147,8 +1144,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     if (alignFrame != null && !headless)
     {
       alignFrame.setProgressBar(MessageManager.formatMessage(
-              "status.saving_file",
-              new String[]
+              "status.saving_file", new Object[]
               { type.getLabel() }), progress);
     }
     try
index 512c418..c0f4949 100644 (file)
@@ -1687,41 +1687,34 @@ public class Desktop extends jalview.jbgui.GDesktop implements
    */
   public static AlignmentViewport[] getViewports(String sequenceSetId)
   {
-    Vector viewp = new Vector();
+    List<AlignmentViewport> viewp = new ArrayList<AlignmentViewport>();
     if (desktop != null)
     {
-      javax.swing.JInternalFrame[] frames = instance.getAllFrames();
+      AlignFrame[] frames = Desktop.getAlignFrames();
 
-      for (int t = 0; t < frames.length; t++)
+      for (AlignFrame afr : frames)
       {
-        if (frames[t] instanceof AlignFrame)
+        if (afr.getViewport().getSequenceSetId().equals(sequenceSetId))
         {
-          AlignFrame afr = ((AlignFrame) frames[t]);
-          if (afr.getViewport().getSequenceSetId().equals(sequenceSetId))
+          if (afr.alignPanels != null)
           {
-            if (afr.alignPanels != null)
+            for (AlignmentPanel ap : afr.alignPanels)
             {
-              for (int a = 0; a < afr.alignPanels.size(); a++)
+              if (sequenceSetId.equals(ap.av.getSequenceSetId()))
               {
-                if (sequenceSetId.equals(afr.alignPanels.get(a).av
-                        .getSequenceSetId()))
-                {
-                  viewp.addElement(afr.alignPanels.get(a).av);
-                }
+                viewp.add(ap.av);
               }
             }
-            else
-            {
-              viewp.addElement(((AlignFrame) frames[t]).getViewport());
-            }
+          }
+          else
+          {
+            viewp.add(afr.getViewport());
           }
         }
       }
       if (viewp.size() > 0)
       {
-        AlignmentViewport[] vp = new AlignmentViewport[viewp.size()];
-        viewp.copyInto(vp);
-        return vp;
+        return viewp.toArray(new AlignmentViewport[viewp.size()]);
       }
     }
     return null;
@@ -2279,6 +2272,8 @@ public class Desktop extends jalview.jbgui.GDesktop implements
 
   /**
    * Accessor method to quickly get all the AlignmentFrames loaded.
+   * 
+   * @return an array of AlignFrame, or null if none found
    */
   public static AlignFrame[] getAlignFrames()
   {
index f08306a..4fb0fe8 100644 (file)
@@ -92,11 +92,13 @@ import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.StringTokenizer;
@@ -182,7 +184,7 @@ public class Jalview2XML
     }
     if (seqRefIds == null)
     {
-      seqRefIds = new Hashtable();
+      seqRefIds = new HashMap<String, SequenceI>();
     }
   }
 
@@ -197,8 +199,12 @@ public class Jalview2XML
    * and alignment sequences. Populated as XML reps of sequence objects are
    * created.)
    */
-  java.util.Hashtable seqRefIds = null; // key->SequenceI resolution
+  Map<String, SequenceI> seqRefIds = null; // key->SequenceI resolution
 
+  /*
+   * Forward referenced sequences - a holding place for cross-references pending
+   * resolution.
+   */
   Vector frefedSequence = null;
 
   boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
@@ -227,7 +233,7 @@ public class Jalview2XML
           {
             if (ref[1] instanceof jalview.datamodel.Mapping)
             {
-              SequenceI seq = (SequenceI) seqRefIds.get(sref);
+              SequenceI seq = seqRefIds.get(sref);
               while (seq.getDatasetSequence() != null)
               {
                 seq = seq.getDatasetSequence();
@@ -238,7 +244,7 @@ public class Jalview2XML
             {
               if (ref[1] instanceof jalview.datamodel.AlignedCodonFrame)
               {
-                SequenceI seq = (SequenceI) seqRefIds.get(sref);
+                SequenceI seq = seqRefIds.get(sref);
                 while (seq.getDatasetSequence() != null)
                 {
                   seq = seq.getDatasetSequence();
@@ -1944,7 +1950,7 @@ public class Jalview2XML
     }
     if (seqRefIds == null)
     {
-      seqRefIds = new Hashtable();
+      seqRefIds = new HashMap<String, SequenceI>();
     }
     if (viewportsAdded == null)
     {
@@ -2384,7 +2390,7 @@ public class Jalview2XML
           AlcodMap[] maps = alc[i].getAlcodMap();
           for (int m = 0; m < maps.length; m++)
           {
-            SequenceI dnaseq = (SequenceI) seqRefIds
+            SequenceI dnaseq = seqRefIds
                     .get(maps[m].getDnasq());
             // Load Mapping
             jalview.datamodel.Mapping mapping = null;
@@ -2662,7 +2668,7 @@ public class Jalview2XML
         for (int s = 0; s < groups[i].getSeqCount(); s++)
         {
           String seqId = groups[i].getSeq(s) + "";
-          jalview.datamodel.SequenceI ts = (jalview.datamodel.SequenceI) seqRefIds
+          jalview.datamodel.SequenceI ts = seqRefIds
                   .get(seqId);
 
           if (ts != null)
@@ -2952,7 +2958,7 @@ public class Jalview2XML
               // Desktop.desktop.getComponentAt(x, y);
               // TODO: NOW: check that this recovers the PDB file correctly.
               String pdbFile = loadPDBFile(jprovider, ids[p].getId());
-              jalview.datamodel.SequenceI seq = (jalview.datamodel.SequenceI) seqRefIds
+              jalview.datamodel.SequenceI seq = seqRefIds
                       .get(JSEQ[i].getId() + "");
               if (sviewid == null)
               {
@@ -3994,9 +4000,8 @@ public class Jalview2XML
   {
     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
     // xRef Codon Maps
-    jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
-            .get(vamsasSeq.getId());
-    jalview.datamodel.SequenceI dsq = null;
+    SequenceI sq = seqRefIds.get(vamsasSeq.getId());
+    SequenceI dsq = null;
     if (sq != null && sq.getDatasetSequence() != null)
     {
       dsq = sq.getDatasetSequence();
@@ -4011,7 +4016,7 @@ public class Jalview2XML
       // need to create or add a new dataset sequence reference to this sequence
       if (sqid != null)
       {
-        dsq = (jalview.datamodel.SequenceI) seqRefIds.get(sqid);
+        dsq = seqRefIds.get(sqid);
       }
       // check again
       if (dsq == null)
@@ -4207,7 +4212,7 @@ public class Jalview2XML
           /**
            * recover from hash
            */
-          jmap.setTo((SequenceI) seqRefIds.get(dsfor));
+          jmap.setTo(seqRefIds.get(dsfor));
         }
         else
         {
@@ -4221,14 +4226,14 @@ public class Jalview2XML
          * local sequence definition
          */
         Sequence ms = mc.getSequence();
-        jalview.datamodel.Sequence djs = null;
+        SequenceI djs = null;
         String sqid = ms.getDsseqid();
         if (sqid != null && sqid.length() > 0)
         {
           /*
            * recover dataset sequence
            */
-          djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
+          djs = seqRefIds.get(sqid);
         }
         else
         {
@@ -4290,6 +4295,8 @@ public class Jalview2XML
     viewportsAdded = new Hashtable();
 
     AlignFrame af = LoadFromObject(jm, null, false, null);
+    // next line added in an attempt to copy AlignedCodonFrame list but failed
+    // resolveFrefedSequences();
     af.alignPanels.clear();
     af.closeMenuItem_actionPerformed(true);
 
@@ -4423,13 +4430,13 @@ public class Jalview2XML
         // register sequence object so the XML parser can recover it.
         if (seqRefIds == null)
         {
-          seqRefIds = new Hashtable();
+          seqRefIds = new HashMap<String, SequenceI>();
         }
         if (seqsToIds == null)
         {
           seqsToIds = new IdentityHashMap();
         }
-        seqRefIds.put(jv2vobj.get(jvobj).toString(), jvobj);
+        seqRefIds.put(jv2vobj.get(jvobj).toString(), (SequenceI) jvobj);
         seqsToIds.put(jvobj, id);
       }
       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
index e433243..37f5d27 100644 (file)
@@ -31,6 +31,7 @@ import javax.swing.JLabel;
 import javax.swing.JMenu;
 import javax.swing.JMenuItem;
 import javax.swing.JPanel;
+import javax.swing.JScrollBar;
 import javax.swing.SwingConstants;
 
 /**
@@ -209,4 +210,51 @@ public final class JvSwingUtils
     }
   }
 
+  /**
+   * Returns the proportion of its range that a scrollbar's position represents,
+   * as a value between 0 and 1. For example if the whole range is from 0 to
+   * 200, then a position of 40 gives proportion = 0.2.
+   * 
+   * @see http://www.javalobby.org/java/forums/t33050.html#91885334
+   * 
+   * @param scroll
+   * @return
+   */
+  public static float getScrollBarProportion(JScrollBar scroll)
+  {
+    /*
+     * The extent (scroll handle width) deduction gives the true operating range
+     * of possible positions.
+     */
+    int possibleRange = scroll.getMaximum() - scroll.getMinimum()
+            - scroll.getModel().getExtent();
+    float valueInRange = scroll.getValue()
+            - (scroll.getModel().getExtent() / 2f);
+    float proportion = valueInRange / possibleRange;
+    return proportion;
+  }
+
+  /**
+   * Returns the scroll bar position in its range that would match the given
+   * proportion (between 0 and 1) of the whole. For example if the whole range
+   * is from 0 to 200, then a proportion of 0.25 gives position 50.
+   * 
+   * @param scrollbar
+   * @param proportion
+   * @return
+   */
+  public static int getScrollValueForProportion(JScrollBar scrollbar,
+          float proportion)
+  {
+    /*
+     * The extent (scroll handle width) deduction gives the true operating range
+     * of possible positions.
+     */
+    float fraction = proportion
+            * (scrollbar.getMaximum() - scrollbar.getMinimum() - scrollbar
+                    .getModel().getExtent())
+            + (scrollbar.getModel().getExtent() / 2f);
+    return Math.min(Math.round(fraction), scrollbar.getMaximum());
+  }
+
 }
index 01dfa3b..215090b 100755 (executable)
  */
 package jalview.gui;
 
-import java.util.*;
-import java.util.List;
-
-import java.awt.*;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
 
-import jalview.datamodel.*;
+import java.awt.Component;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
 
 /**
  * Route datamodel/view update events for a sequence set to any display
@@ -36,57 +39,61 @@ import jalview.datamodel.*;
  */
 public class PaintRefresher
 {
-  static Hashtable components;
+  static Map<String, List<Component>> components = new HashMap<String, List<Component>>();
 
   /**
-   * DOCUMENT ME!
+   * Add the given component to those registered under the given sequence set
+   * id. Does nothing if already added.
    * 
    * @param comp
-   *          DOCUMENT ME!
    * @param al
-   *          DOCUMENT ME!
    */
   public static void Register(Component comp, String seqSetId)
   {
-    if (components == null)
-    {
-      components = new Hashtable();
-    }
-
     if (components.containsKey(seqSetId))
     {
-      Vector comps = (Vector) components.get(seqSetId);
+      List<Component> comps = components.get(seqSetId);
       if (!comps.contains(comp))
       {
-        comps.addElement(comp);
+        comps.add(comp);
       }
     }
     else
     {
-      Vector vcoms = new Vector();
-      vcoms.addElement(comp);
+      List<Component> vcoms = new ArrayList<Component>();
+      vcoms.add(comp);
       components.put(seqSetId, vcoms);
     }
   }
 
+  /**
+   * Remove this component from all registrations. Also removes a registered
+   * sequence set id if there are no remaining components registered against it.
+   * 
+   * @param comp
+   */
   public static void RemoveComponent(Component comp)
   {
-    if (components == null)
-    {
-      return;
-    }
-
-    Enumeration en = components.keys();
-    while (en.hasMoreElements())
+    List<String> emptied = new ArrayList<String>();
+    for (Entry<String, List<Component>> registered : components.entrySet())
     {
-      String id = en.nextElement().toString();
-      Vector comps = (Vector) components.get(id);
+      String id = registered.getKey();
+      List<Component> comps = components.get(id);
       comps.remove(comp);
-      if (comps.size() == 0)
+      if (comps.isEmpty())
       {
-        components.remove(id);
+        emptied.add(id);
       }
     }
+
+    /*
+     * Remove now empty ids after the above (to avoid
+     * ConcurrentModificationException).
+     */
+    for (String id : emptied)
+    {
+      components.remove(id);
+    }
   }
 
   public static void Refresh(Component source, String id)
@@ -97,24 +104,15 @@ public class PaintRefresher
   public static void Refresh(Component source, String id,
           boolean alignmentChanged, boolean validateSequences)
   {
-    if (components == null)
-    {
-      return;
-    }
-
-    Component comp;
-    Vector comps = (Vector) components.get(id);
+    List<Component> comps = components.get(id);
 
     if (comps == null)
     {
       return;
     }
 
-    Enumeration e = comps.elements();
-    while (e.hasMoreElements())
+    for (Component comp : comps)
     {
-      comp = (Component) e.nextElement();
-
       if (comp == source)
       {
         continue;
@@ -242,30 +240,20 @@ public class PaintRefresher
 
   static AlignmentPanel[] getAssociatedPanels(String id)
   {
-    if (components == null)
-    {
-      return new AlignmentPanel[0];
-    }
-    ;
-    Vector comps = (Vector) components.get(id);
+    List<Component> comps = components.get(id);
     if (comps == null)
     {
       return new AlignmentPanel[0];
     }
-    ;
-    Vector tmp = new Vector();
-    int i, iSize = comps.size();
-    for (i = 0; i < iSize; i++)
+    List<AlignmentPanel> tmp = new ArrayList<AlignmentPanel>();
+    for (Component comp : comps)
     {
-      if (comps.elementAt(i) instanceof AlignmentPanel)
+      if (comp instanceof AlignmentPanel)
       {
-        tmp.addElement(comps.elementAt(i));
+        tmp.add((AlignmentPanel) comp);
       }
     }
-    AlignmentPanel[] result = new AlignmentPanel[tmp.size()];
-    tmp.toArray(result);
-
-    return result;
+    return tmp.toArray(new AlignmentPanel[tmp.size()]);
   }
 
 }
index 24babda..f98eea7 100644 (file)
@@ -15,6 +15,7 @@ import java.util.Map.Entry;
 
 import javax.swing.AbstractAction;
 import javax.swing.JComponent;
+import javax.swing.JMenuItem;
 import javax.swing.KeyStroke;
 import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
@@ -142,7 +143,7 @@ public class SplitFrame extends GSplitFrame
   {
     if (getTopComponent() instanceof AlignFrame)
     {
-      for (Entry<KeyStroke, ActionListener> acc : ((AlignFrame) getTopComponent())
+      for (Entry<KeyStroke, JMenuItem> acc : ((AlignFrame) getTopComponent())
               .getAccelerators().entrySet())
       {
 
@@ -156,8 +157,12 @@ public class SplitFrame extends GSplitFrame
             Component c = getComponentAtMouse();
             if (c instanceof AlignFrame)
             {
-              ((AlignFrame) c).getAccelerators().get(ks)
-                      .actionPerformed(null);
+              for (ActionListener a : ((AlignFrame) c).getAccelerators()
+                      .get(ks).getActionListeners())
+              {
+
+                a.actionPerformed(null);
+              }
             }
           }
         });
@@ -166,8 +171,49 @@ public class SplitFrame extends GSplitFrame
        * Disable unwanted here
        */
       // X expand views - wrecks the split pane view
-      this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove(
-              KeyStroke.getKeyStroke(KeyEvent.VK_X, 0, false));
+      KeyStroke key_X = KeyStroke.getKeyStroke(KeyEvent.VK_X, 0, false);
+      disableAccelerator(key_X);
+    }
+  }
+
+  /**
+   * Ugly hack for Proof of Concept that disables the key binding in this frame
+   * _and_ disables the bound menu item _and_ removes the key accelerator in the
+   * child frames.
+   * 
+   * @param key
+   */
+  protected void disableAccelerator(KeyStroke key)
+  {
+    disableAccelerator(key, getTopComponent());
+    disableAccelerator(key, getBottomComponent());
+    this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove(key);
+  }
+
+  /**
+   * Disable the menu item for which this key is the accelerator, also removes
+   * its action listeners to prevent key accelerator working.
+   * 
+   * @param key
+   * @param comp
+   */
+  private void disableAccelerator(KeyStroke key, JComponent comp)
+  {
+    // HACKED ONLY FOR PROOF OF CONCEPT
+    // Proper solution might involve explicit 'configure menu' method on
+    // AlignFrame, or
+    // changing key listeners to key bindings in AlignFrame, or both
+    if (comp instanceof AlignFrame)
+    {
+      JMenuItem mi = ((AlignFrame) comp).getAccelerators().get(key);
+      if (mi != null)
+      {
+        mi.setEnabled(false);
+        for (ActionListener al : mi.getActionListeners())
+        {
+          mi.removeActionListener(al);
+        }
+      }
     }
   }
 }
index 6f963a5..fdb7641 100755 (executable)
@@ -390,7 +390,7 @@ public class GAlignFrame extends JInternalFrame
 
   private boolean showAutoCalculatedAbove = false;
 
-  private Map<KeyStroke, ActionListener> accelerators = new HashMap<KeyStroke, ActionListener>();
+  private Map<KeyStroke, JMenuItem> accelerators = new HashMap<KeyStroke, JMenuItem>();
 
   public GAlignFrame()
   {
@@ -2476,7 +2476,7 @@ public class GAlignFrame extends JInternalFrame
           JMenuItem menuItem, ActionListener actionListener)
   {
     menuItem.setAccelerator(keyStroke);
-    accelerators.put(keyStroke, actionListener);
+    accelerators.put(keyStroke, menuItem);
     menuItem.addActionListener(actionListener);
   }
 
@@ -3210,7 +3210,7 @@ public class GAlignFrame extends JInternalFrame
     this.annotationSortOrder = annotationSortOrder;
   }
 
-  public Map<KeyStroke, ActionListener> getAccelerators()
+  public Map<KeyStroke, JMenuItem> getAccelerators()
   {
     return this.accelerators;
   }
index c1e050f..0544864 100644 (file)
@@ -1,5 +1,6 @@
 package jalview.util;
 
+
 public class StringUtils
 {
 
@@ -79,4 +80,27 @@ public class StringUtils
     }
     return tmp;
   }
+
+  /**
+   * Returns the last part of 'input' after the last occurrence of 'token'. For
+   * example to extract only the filename from a full path or URL.
+   * 
+   * @param input
+   * @param token
+   *          a delimiter which must be in regular expression format
+   * @return
+   */
+  public static String getLastToken(String input, String token)
+  {
+    if (input == null)
+    {
+      return null;
+    }
+    if (token == null)
+    {
+      return input;
+    }
+    String[] st = input.split(token);
+    return st[st.length - 1];
+  }
 }
index dbd063c..3b3d926 100644 (file)
@@ -105,6 +105,35 @@ public class AlignmentTest
     assertFalse(iter.hasNext());
   }
 
+  @Test
+  public void testDeleteAllAnnotations_includingAutocalculated()
+  {
+    AlignmentAnnotation aa = new AlignmentAnnotation("Consensus",
+            "Consensus", 0.5);
+    aa.autoCalculated = true;
+    al.addAnnotation(aa);
+    AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
+    assertEquals("Wrong number of annotations before deleting", 4,
+            anns.length);
+    al.deleteAllAnnotations(true);
+    assertEquals("Not all deleted", 0, al.getAlignmentAnnotation().length);
+  }
+
+  @Test
+  public void testDeleteAllAnnotations_excludingAutocalculated()
+  {
+    AlignmentAnnotation aa = new AlignmentAnnotation("Consensus",
+            "Consensus", 0.5);
+    aa.autoCalculated = true;
+    al.addAnnotation(aa);
+    AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
+    assertEquals("Wrong number of annotations before deleting", 4,
+            anns.length);
+    al.deleteAllAnnotations(false);
+    assertEquals("Not just one annotation left", 1,
+            al.getAlignmentAnnotation().length);
+  }
+
   /**
    * Tests for realigning as per a supplied alignment: Dna as Dna.
    * 
@@ -134,7 +163,7 @@ public class AlignmentTest
     acf.addMap(al2.getSequenceAt(1), al1.getSequenceAt(1), ml);
     al1.addCodonFrame(acf);
 
-    al2.alignAs(al1);
+    ((Alignment) al2).alignAs(al1, false, true);
     assertEquals("GC-TC--GUC-GTA-CT", al2.getSequenceAt(0)
             .getSequenceAsString());
     assertEquals("-GG-GTC--AGG---CAGT", al2.getSequenceAt(1)
@@ -154,7 +183,7 @@ public class AlignmentTest
     String before0 = al2.getSequenceAt(0).getSequenceAsString();
     String before1 = al2.getSequenceAt(1).getSequenceAsString();
 
-    al2.alignAs(al1);
+    ((Alignment) al2).alignAs(al1, false, true);
     assertEquals(before0, al2.getSequenceAt(0).getSequenceAsString());
     assertEquals(before1, al2.getSequenceAt(1).getSequenceAsString());
   }
@@ -180,38 +209,13 @@ public class AlignmentTest
     acf.addMap(al1.getSequenceAt(1), al2.getSequenceAt(1), ml);
     al2.addCodonFrame(acf);
 
-    al1.alignAs(al2);
-    assertEquals("AC-G---G--CUC-CA------A-CT", al1.getSequenceAt(0)
-            .getSequenceAsString());
-    assertEquals("---CG-T---TA--ACG---A---AGT", al1.getSequenceAt(1)
-            .getSequenceAsString());
-  }
-
-  /**
-   * Test aligning cdna (with introns) as per protein alignment.
-   * 
-   * @throws IOException
-   */
-  @Test
-  public void testAlignAs_cdnaAsProteinWithIntrons() throws IOException
-  {
     /*
-     * Load alignments and add mappings for cDNA to protein
+     * Realign DNA; currently keeping existing gaps in introns only
      */
-    AlignmentI al1 = loadAlignment(CDNA_SEQS_1, "FASTA");
-    AlignmentI al2 = loadAlignment(AA_SEQS_1, "FASTA");
-    AlignedCodonFrame acf = new AlignedCodonFrame();
-    MapList ml = new MapList(new int[]
-    { 1, 12 }, new int[]
-    { 1, 4 }, 3, 1);
-    acf.addMap(al1.getSequenceAt(0), al2.getSequenceAt(0), ml);
-    acf.addMap(al1.getSequenceAt(1), al2.getSequenceAt(1), ml);
-    al2.addCodonFrame(acf);
-
-    al1.alignAs(al2);
-    assertEquals("AC-G---G--CUC-CA------A-CT", al1.getSequenceAt(0)
+    ((Alignment) al1).alignAs(al2, false, true);
+    assertEquals("ACG---GCUCCA------ACT", al1.getSequenceAt(0)
             .getSequenceAsString());
-    assertEquals("---CG-T---TA--ACG---A---AGT", al1.getSequenceAt(1)
+    assertEquals("---CGT---TAACGA---AGT", al1.getSequenceAt(1)
             .getSequenceAsString());
   }
 
@@ -252,7 +256,8 @@ public class AlignmentTest
     ((Alignment) al1).alignAs(al2, false, false);
     assertEquals("---AAagG------GCCcTTT", al1.getSequenceAt(0)
             .getSequenceAsString());
-    assertEquals("cCCGgg---TTT------AAA", al1.getSequenceAt(1)
+    // note 1 gap in protein corresponds to 'gg-' in DNA (3 positions)
+    assertEquals("cCCGgg-TTT------AAA", al1.getSequenceAt(1)
             .getSequenceAsString());
 
     /*
@@ -266,7 +271,7 @@ public class AlignmentTest
     // assumption: we include 'the greater of' protein/dna gap lengths, not both
     assertEquals("---A-Aa-gG------GCC-cT-TT", al1.getSequenceAt(0)
             .getSequenceAsString());
-    assertEquals("c--CCGgg---TT--T------AA-A", al1.getSequenceAt(1)
+    assertEquals("c--CCGgg-TT--T------AA-A", al1.getSequenceAt(1)
             .getSequenceAsString());
   }
 }
diff --git a/test/jalview/gui/JvSwingUtilsTest.java b/test/jalview/gui/JvSwingUtilsTest.java
new file mode 100644 (file)
index 0000000..11e6ea5
--- /dev/null
@@ -0,0 +1,41 @@
+package jalview.gui;
+
+import static org.junit.Assert.assertEquals;
+
+import javax.swing.JScrollBar;
+
+import org.junit.Test;
+
+public class JvSwingUtilsTest
+{
+
+  @Test
+  public void testGetScrollBarProportion()
+  {
+    /*
+     * orientation, value, extent (width), min, max
+     */
+    JScrollBar sb = new JScrollBar(0, 125, 50, 0, 450);
+
+    /*
+     * operating range is 25 - 425 (400 wide) so value 125 is 100/400ths of this
+     * range
+     */
+    assertEquals(0.25f, JvSwingUtils.getScrollBarProportion(sb), 0.001f);
+  }
+
+  @Test
+  public void testGetScrollValueForProportion()
+  {
+    /*
+     * orientation, value, extent (width), min, max
+     */
+    JScrollBar sb = new JScrollBar(0, 125, 50, 0, 450);
+
+    /*
+     * operating range is 25 - 425 (400 wide) so value 125 is a quarter of this
+     * range
+     */
+    assertEquals(125, JvSwingUtils.getScrollValueForProportion(sb, 0.25f));
+  }
+}
diff --git a/test/jalview/gui/PaintRefresherTest.java b/test/jalview/gui/PaintRefresherTest.java
new file mode 100644 (file)
index 0000000..2737dd0
--- /dev/null
@@ -0,0 +1,115 @@
+package jalview.gui;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+
+import java.awt.Component;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JPanel;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class PaintRefresherTest
+{
+  // TODO would prefer PaintRefresher to be a single rather than static
+  @Before
+  public void setUp()
+  {
+    PaintRefresher.components.clear();
+  }
+
+  @After
+  public void tearDown()
+  {
+    PaintRefresher.components.clear();
+  }
+
+  @Test
+  public void testRegister()
+  {
+    JPanel jp = new JPanel();
+    JPanel jp2 = new JPanel();
+    JPanel jp3 = new JPanel();
+    JPanel jp4 = new JPanel();
+    PaintRefresher.Register(jp, "22");
+    PaintRefresher.Register(jp, "22");
+    PaintRefresher.Register(jp2, "22");
+    PaintRefresher.Register(jp3, "33");
+    PaintRefresher.Register(jp3, "44");
+    PaintRefresher.Register(jp4, "44");
+
+    Map<String, List<Component>> registered = PaintRefresher.components;
+    assertEquals(3, registered.size());
+    assertEquals(2, registered.get("22").size());
+    assertEquals(1, registered.get("33").size());
+    assertEquals(2, registered.get("44").size());
+    assertTrue(registered.get("22").contains(jp));
+    assertTrue(registered.get("22").contains(jp2));
+    assertTrue(registered.get("33").contains(jp3));
+    assertTrue(registered.get("44").contains(jp3));
+    assertTrue(registered.get("44").contains(jp4));
+  }
+
+  @Test
+  public void testRemoveComponent()
+  {
+    Map<String, List<Component>> registered = PaintRefresher.components;
+    
+    // no error with an empty PaintRefresher
+    JPanel jp = new JPanel();
+    JPanel jp2 = new JPanel();
+    PaintRefresher.RemoveComponent(jp);
+    assertTrue(registered.isEmpty());
+
+    /*
+     * Add then remove one item
+     */
+    PaintRefresher.Register(jp, "11");
+    PaintRefresher.RemoveComponent(jp);
+    assertTrue(registered.isEmpty());
+
+    /*
+     * Add one item under two ids, then remove it. It is removed from both ids,
+     * and the now empty id is removed.
+     */
+    PaintRefresher.Register(jp, "11");
+    PaintRefresher.Register(jp, "22");
+    PaintRefresher.Register(jp2, "22");
+    PaintRefresher.RemoveComponent(jp);
+    // "11" is removed as now empty, only 22/jp2 left
+    assertEquals(1, registered.size());
+    assertEquals(1, registered.get("22").size());
+    assertTrue(registered.get("22").contains(jp2));
+  }
+
+  @Test
+  public void testGetAssociatedPanels()
+  {
+    SequenceI [] seqs = new SequenceI[]{new Sequence("", "ABC")};
+    Alignment al = new Alignment(seqs);
+
+    /*
+     * AlignFrame constructor has side-effects: AlignmentPanel is constructed,
+     * and SeqCanvas, IdPanel, AlignmentPanel are all registered under the
+     * sequence set id of the viewport.
+     */
+    AlignViewport av = new AlignViewport(al);
+    AlignFrame af = new AlignFrame(al, 4, 1);
+    AlignmentPanel ap1 = af.alignPanel;
+    AlignmentPanel[] panels = PaintRefresher.getAssociatedPanels(av
+            .getSequenceSetId());
+    assertEquals(1, panels.length);
+    assertSame(ap1, panels[0]);
+    
+    panels = PaintRefresher.getAssociatedPanels(av.getSequenceSetId() + 1);
+    assertEquals(0, panels.length);
+  }
+}
index eba2da4..22a4130 100644 (file)
@@ -1,5 +1,7 @@
 package jalview.util;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.util.Arrays;
@@ -53,4 +55,17 @@ public class StringUtilsTest
     // out of range deletion is ignore
     assertTrue(Arrays.equals(c1, StringUtils.deleteChars(c1, 3, 4)));
   }
+
+  @Test
+  public void testGetLastToken()
+  {
+    assertNull(StringUtils.getLastToken(null, null));
+    assertNull(StringUtils.getLastToken(null, "/"));
+    assertEquals("a", StringUtils.getLastToken("a", null));
+
+    assertEquals("abc", StringUtils.getLastToken("abc", "/"));
+    assertEquals("c", StringUtils.getLastToken("abc", "b"));
+    assertEquals("file1.dat", StringUtils.getLastToken(
+            "file://localhost:8080/data/examples/file1.dat", "/"));
+  }
 }