Merge branch 'develop' into features/JAL-2446NCList
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 22 May 2017 14:16:40 +0000 (15:16 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 22 May 2017 14:16:40 +0000 (15:16 +0100)
Conflicts:
src/jalview/analysis/AlignmentSorter.java
src/jalview/appletgui/FeatureSettings.java
src/jalview/appletgui/SeqPanel.java
src/jalview/gui/FeatureRenderer.java
src/jalview/gui/FeatureSettings.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
test/jalview/analysis/AlignmentSorterTest.java

1  2 
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/appletgui/FeatureSettings.java
src/jalview/appletgui/SeqPanel.java
src/jalview/gui/CutAndPasteTransfer.java
src/jalview/gui/FeatureRenderer.java
src/jalview/gui/FeatureSettings.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java

Simple merge
Simple merge
@@@ -371,35 -370,43 +369,43 @@@ public class FeatureSettings extends Pa
    // Group selection states
    void resetTable(boolean groupsChanged)
    {
 -    SequenceFeature[] tmpfeatures;
 -    String group = null, type;
 -    Vector<String> visibleChecks = new Vector<String>();
 +    List<String> displayableTypes = new ArrayList<String>();
+     Set<String> foundGroups = new HashSet<String>();
++
      AlignmentI alignment = av.getAlignment();
      for (int i = 0; i < alignment.getHeight(); i++)
      {
 -      if (alignment.getSequenceAt(i).getSequenceFeatures() == null)
 -      {
 -        continue;
 -      }
 +      SequenceI seq = alignment.getSequenceAt(i);
  
 -      tmpfeatures = alignment.getSequenceAt(i).getSequenceFeatures();
 -      int index = 0;
 -      while (index < tmpfeatures.length)
 +      /*
 +       * get the sequence's groups for positional features
 +       * and keep track of which groups are visible
 +       */
 +      Set<String> groups = seq.getFeatures().getFeatureGroups(true);
 +      Set<String> visibleGroups = new HashSet<String>();
 +      for (String group : groups)
        {
 -        group = tmpfeatures[index].featureGroup;
 -        foundGroups.add(group);
 -
 -        if (group == null || checkGroupState(group))
 +        if (group == null || fr.checkGroupVisibility(group, true))
          {
 -          type = tmpfeatures[index].getType();
 -          if (!visibleChecks.contains(type))
 -          {
 -            visibleChecks.addElement(type);
 -          }
 +          visibleGroups.add(group);
          }
 -        index++;
        }
 +
 +      /*
 +       * get distinct feature types for visible groups
 +       * record distinct visible types
 +       */
 +      Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
 +              visibleGroups.toArray(new String[visibleGroups.size()]));
 +      displayableTypes.addAll(types);
      }
  
+     /*
+      * remove any checkboxes for groups not present
+      */
+     pruneGroups(foundGroups);
      Component[] comps;
      int cSize = featurePanel.getComponentCount();
      MyCheckbox check;
@@@ -563,20 -562,20 +563,19 @@@ public class SeqPanel extends Panel imp
          av.setSelectionGroup(null);
        }
  
 -      SequenceFeature[] features = findFeaturesAtRes(sequence,
 +      List<SequenceFeature> features = findFeaturesAtRes(sequence,
                sequence.findPosition(findRes(evt)));
  
 -      if (features != null && features.length > 0)
 +      if (!features.isEmpty())
        {
          SearchResultsI highlight = new SearchResults();
 -        highlight.addResult(sequence, features[0].getBegin(),
 -                features[0].getEnd());
 +        highlight.addResult(sequence, features.get(0).getBegin(), features
 +                .get(0).getEnd());
          seqCanvas.highlightSearchResults(highlight);
 -      }
 -      if (features != null && features.length > 0)
 -      {
 +        SequenceFeature[] featuresArray = features
 +                .toArray(new SequenceFeature[features.size()]);
          seqCanvas.getFeatureRenderer().amendFeatures(
-                 new SequenceI[] { sequence }, featuresArray, false, ap,
-                 null);
 -                new SequenceI[] { sequence }, features, false, ap);
++                new SequenceI[] { sequence }, featuresArray, false, ap);
  
          seqCanvas.highlightSearchResults(null);
        }
@@@ -298,6 -298,6 +298,7 @@@ public class CutAndPasteTransfer extend
                    AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
            af.getViewport().setShowSequenceFeatures(showSeqFeatures);
            af.getViewport().setFeaturesDisplayed(fd);
++          af.setMenusForViewport();
            ColourSchemeI cs = ColourSchemeMapper.getJalviewColourScheme(
                    colourSchemeName, al);
            if (cs != null)
@@@ -405,53 -420,29 +421,55 @@@ public class FeatureRenderer extend
        {
          /*
           * YES_OPTION corresponds to the Amend button
+          * need to refresh Feature Settings if type, group or colour changed
           */
-         boolean typeChanged = !lastFeatureAdded.equals(sf.type);
-         String newType = lastFeatureAdded;
-         String newFeatureGroup = lastFeatureGroupAdded;
-         String newDescription = lastDescriptionAdded;
 -        sf.type = name.getText().trim();
 -        sf.featureGroup = group.getText().trim();
 -        sf.description = description.getText().replaceAll("\n", " ");
++        String newType = name.getText().trim();
++        String newFeatureGroup = group.getText().trim();
++        String newDescription = description.getText().replaceAll("\n", " ");
+         boolean refreshSettings = (!featureType.equals(sf.type) || !featureGroup
+                 .equals(sf.featureGroup));
+         refreshSettings |= (fcol != oldcol);
 -
 -        setColour(sf.type, fcol);
 -
 +        setColour(newType, fcol);
-         getFeaturesDisplayed().setVisible(newType);
++/*?*/        getFeaturesDisplayed().setVisible(newType);
 +        int newBegin = sf.begin;
 +        int newEnd = sf.end;
          try
          {
 -          sf.begin = ((Integer) start.getValue()).intValue();
 -          sf.end = ((Integer) end.getValue()).intValue();
 +          newBegin = ((Integer) start.getValue()).intValue();
 +          newEnd = ((Integer) end.getValue()).intValue();
          } catch (NumberFormatException ex)
          {
 +          // JSpinner doesn't accept invalid format data :-)
 +        }
 +
 +        /*
 +         * replace the feature by deleting it and adding a new one
 +         * (to ensure integrity of SequenceFeatures data store)
 +         */
 +        sequences.get(0).deleteFeature(sf);
 +        SequenceFeature newSf = new SequenceFeature(newType,
 +                newDescription, newBegin, newEnd, sf.getScore(),
 +                newFeatureGroup);
 +        // ensure any additional properties are copied
 +        if (sf.otherDetails != null)
 +        {
 +          newSf.otherDetails = new HashMap<String, Object>(sf.otherDetails);
 +        }
 +        ffile.parseDescriptionHTML(newSf, false);
 +        // add any additional links not parsed from description
 +        if (sf.links != null)
 +        {
 +          for (String link : sf.links)
 +          {
 +            newSf.addLink(link);
 +          }
          }
 +        // amend features only gets one sequence to act on
 +        sequences.get(0).addSequenceFeature(newSf);
  
-         if (typeChanged)
 -        ffile.parseDescriptionHTML(sf, false);
+         if (refreshSettings)
          {
-           findAllFeatures();
+           featuresAdded();
          }
        }
      }
@@@ -563,53 -580,66 +556,55 @@@ public class FeatureSettings extends JP
      typeWidth = new Hashtable<String, float[]>();
      // TODO: change avWidth calculation to 'per-sequence' average and use long
      // rather than float
 -    float[] avWidth = null;
 -    SequenceFeature[] tmpfeatures;
 -    String group = null, type;
 -    Vector<String> visibleChecks = new Vector<String>();
 +
 +    Set<String> displayableTypes = new HashSet<String>();
+     Set<String> foundGroups = new HashSet<String>();
  
 -    // Find out which features should be visible depending on which groups
 -    // are selected / deselected
 -    // and recompute average width ordering
 +    /*
 +     * determine which feature types may be visible depending on 
 +     * which groups are selected, and recompute average width data
 +     */
      for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
      {
  
 -      tmpfeatures = af.getViewport().getAlignment().getSequenceAt(i)
 -              .getSequenceFeatures();
 -      if (tmpfeatures == null)
 -      {
 -        continue;
 -      }
 +      SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
  
 -      int index = 0;
 -      while (index < tmpfeatures.length)
 +      /*
 +       * get the sequence's groups for positional features
 +       * and keep track of which groups are visible
 +       */
 +      Set<String> groups = seq.getFeatures().getFeatureGroups(true);
 +      Set<String> visibleGroups = new HashSet<String>();
 +      for (String group : groups)
        {
 -        group = tmpfeatures[index].featureGroup;
 -        foundGroups.add(group);
 -
 -        if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
 -        {
 -          index++;
 -          continue;
 -        }
 -
          if (group == null || checkGroupState(group))
          {
 -          type = tmpfeatures[index].getType();
 -          if (!visibleChecks.contains(type))
 -          {
 -            visibleChecks.addElement(type);
 -          }
 -        }
 -        if (!typeWidth.containsKey(tmpfeatures[index].getType()))
 -        {
 -          typeWidth.put(tmpfeatures[index].getType(),
 -                  avWidth = new float[3]);
 +          visibleGroups.add(group);
          }
 -        else
 -        {
 -          avWidth = typeWidth.get(tmpfeatures[index].getType());
 -        }
 -        avWidth[0]++;
 -        if (tmpfeatures[index].getBegin() > tmpfeatures[index].getEnd())
 -        {
 -          avWidth[1] += 1 + tmpfeatures[index].getBegin()
 -                  - tmpfeatures[index].getEnd();
 -        }
 -        else
 +      }
++      foundGroups.addAll(groups);
 +
 +      /*
 +       * get distinct feature types for visible groups
 +       * record distinct visible types, and their count and total length
 +       */
 +      Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
 +              visibleGroups.toArray(new String[visibleGroups.size()]));
 +      for (String type : types)
 +      {
 +        displayableTypes.add(type);
 +        float[] avWidth = typeWidth.get(type);
 +        if (avWidth == null)
          {
 -          avWidth[1] += 1 + tmpfeatures[index].getEnd()
 -                  - tmpfeatures[index].getBegin();
 +          avWidth = new float[2];
 +          typeWidth.put(type, avWidth);
          }
 -        index++;
 +        // todo this could include features with a non-visible group
 +        // - do we greatly care?
 +        // todo should we include non-displayable features here, and only
 +        // update when features are added?
 +        avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
 +        avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
        }
      }
  
@@@ -330,62 -341,101 +330,96 @@@ public abstract class FeatureRendererMo
          }
        }
      }
 -    if (minmax == null)
 -    {
 -      minmax = new Hashtable<String, float[][]>();
 -    }
 -
 -    Set<String> oldGroups = new HashSet<String>(featureGroups.keySet());
++    // <<<<<<< HEAD
++    //
++    // =======
++    // if (minmax == null)
++    // {
++    // minmax = new Hashtable<String, float[][]>();
++    // }
++    //
++    // Set<String> oldGroups = new HashSet<String>(featureGroups.keySet());
++    // >>>>>>> refs/heads/develop
      AlignmentI alignment = av.getAlignment();
 +    List<String> allfeatures = new ArrayList<String>(); // or HashSet?
 +
      for (int i = 0; i < alignment.getHeight(); i++)
      {
        SequenceI asq = alignment.getSequenceAt(i);
 -      SequenceFeature[] features = asq.getSequenceFeatures();
 -
 -      if (features == null)
 +      for (String group : asq.getFeatures().getFeatureGroups(true))
        {
 -        continue;
 -      }
 -
 -      int index = 0;
 -      while (index < features.length)
 -      {
 -        String fgrp = features[index].getFeatureGroup();
 -        oldGroups.remove(fgrp);
 -        if (!featuresDisplayed.isRegistered(features[index].getType()))
++        // <<<<<<< HEAD
 +        /*
 +         * features in null group are always displayed; other groups
 +         * keep their current visibility; new groups as 'newMadeVisible'
 +         */
 +        boolean groupDisplayed = true;
 +        if (group != null)
++        // =======
++        // continue;
++        // }
++        //
++        // int index = 0;
++        // while (index < features.length)
++        // {
++        // String fgrp = features[index].getFeatureGroup();
++        // oldGroups.remove(fgrp);
++        // if (!featuresDisplayed.isRegistered(features[index].getType()))
++        // >>>>>>> refs/heads/develop
          {
 -          if (fgrp != null)
++          // <<<<<<< HEAD
 +          if (featureGroups.containsKey(group))
++          // =======
++          // if (fgrp != null)
++          // >>>>>>> refs/heads/develop
            {
 -            Boolean groupDisplayed = featureGroups.get(fgrp);
 -            if (groupDisplayed == null)
 -            {
 -              groupDisplayed = Boolean.valueOf(newMadeVisible);
 -              featureGroups.put(fgrp, groupDisplayed);
 -            }
 -            if (!groupDisplayed.booleanValue())
 -            {
 -              index++;
 -              continue;
 -            }
 +            groupDisplayed = featureGroups.get(group);
            }
 -          if (!(features[index].begin == 0 && features[index].end == 0))
 +          else
            {
 -            // If beginning and end are 0, the feature is for the whole sequence
 -            // and we don't want to render the feature in the normal way
 -
 -            if (newMadeVisible
 -                    && !oldfeatures.contains(features[index].getType()))
 -            {
 -              // this is a new feature type on the alignment. Mark it for
 -              // display.
 -              featuresDisplayed.setVisible(features[index].getType());
 -              setOrder(features[index].getType(), 0);
 -            }
 +            groupDisplayed = newMadeVisible;
 +            featureGroups.put(group, groupDisplayed);
            }
          }
 -        if (!allfeatures.contains(features[index].getType()))
 +        if (groupDisplayed)
          {
 -          allfeatures.add(features[index].getType());
 -        }
 -        if (!Float.isNaN(features[index].score))
 -        {
 -          int nonpos = features[index].getBegin() >= 1 ? 0 : 1;
 -          float[][] mm = minmax.get(features[index].getType());
 -          if (mm == null)
 -          {
 -            mm = new float[][] { null, null };
 -            minmax.put(features[index].getType(), mm);
 -          }
 -          if (mm[nonpos] == null)
 -          {
 -            mm[nonpos] = new float[] { features[index].score,
 -                features[index].score };
 -
 -          }
 -          else
 +          Set<String> types = asq.getFeatures().getFeatureTypesForGroups(
 +                  true, group);
 +          for (String type : types)
            {
 -            if (mm[nonpos][0] > features[index].score)
 -            {
 -              mm[nonpos][0] = features[index].score;
 -            }
 -            if (mm[nonpos][1] < features[index].score)
 +            if (!allfeatures.contains(type)) // or use HashSet and no test?
              {
 -              mm[nonpos][1] = features[index].score;
 +              allfeatures.add(type);
              }
 +            updateMinMax(asq, type, true); // todo: for all features?
            }
          }
 -        index++;
        }
      }
  
      /*
 -     * oldGroups now consists of groups that no longer 
 -     * have any feature in them - remove these
++    //<<<<<<< HEAD
 +     * mark any new feature types as visible
       */
 -    for (String grp : oldGroups)
 +    Collections.sort(allfeatures, String.CASE_INSENSITIVE_ORDER);
 +    if (newMadeVisible)
      {
 -      featureGroups.remove(grp);
 +      for (String type : allfeatures)
 +      {
 +        if (!oldfeatures.contains(type))
 +        {
 +          featuresDisplayed.setVisible(type);
 +          setOrder(type, 0);
 +        }
 +      }
++      // =======
++      // * oldGroups now consists of groups that no longer
++      // * have any feature in them - remove these
++      // */
++      // for (String grp : oldGroups)
++      // {
++      // featureGroups.remove(grp);
++      // >>>>>>> refs/heads/develop
      }
  
      updateRenderOrder(allfeatures);