Merge branch 'develop' into trialMerge
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 6 Jun 2019 14:10:48 +0000 (15:10 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Thu, 6 Jun 2019 14:10:48 +0000 (15:10 +0100)
1  2 
resources/lang/Messages.properties
resources/lang/Messages_es.properties
src/jalview/bin/Jalview.java
src/jalview/gui/AlignFrame.java
src/jalview/gui/FeatureTypeSettings.java

@@@ -801,7 -801,6 +801,7 @@@ label.rest_client_submit = {0} using {1
  label.fetch_retrieve_from =Retrieve from {0}</html>
  label.fetch_retrieve_from_all_sources = Retrieve from all {0} sources in {1}<br>First is :{2}<html> 
  label.feature_settings_click_drag = Drag up or down to change render order.<br/>Double click to select columns containing feature.
 +label.feature_settings_select_columns = Double click to select columns containing feature
  label.transparency_tip = Adjust transparency to 'see through' feature colours.
  label.opt_and_params_further_details = see further details by right-clicking
  label.opt_and_params_show_brief_desc_image_link = <html>Click to show brief description<br><img src="{0}"/> Right click for further information.</html> 
@@@ -1340,10 -1339,6 +1340,10 @@@ label.most_bound_molecules = Most Boun
  label.most_polymer_residues = Most Polymer Residues
  label.cached_structures = Cached Structures
  label.free_text_search = Free Text Search
 +label.summary_view = Summary View
 +label.group_by_so = Group features by Sequence Ontology
 +label.apply_to_subtypes = Apply to features and sub-types of
 +label.apply_also_to = Apply also to:
  label.backupfiles_confirm_delete = Confirm delete
  label.backupfiles_confirm_delete_old_files = Delete the following older backup files? (see the Backups tab in Preferences for more options)
  label.backupfiles_confirm_save_file = Confirm save file
@@@ -1359,12 -1354,13 +1359,13 @@@ label.append_to_filename = Append to fi
  label.append_to_filename_tooltip = %n in the text will be replaced by the backup number. The text will appear after the filename. See the summary box above.
  label.index_digits = Number of digits to use for the backup number (%n)
  label.summary_of_backups_scheme = Summary of backup scheme
+ label.scheme_examples = Scheme examples
  label.increment_index = Increase appended text numbers - newest file has largest number.
  label.reverse_roll = "Roll" appended text numbers - newest backup file is always number 1.
  label.keep_files = Deleting old backup files
  label.keep_all_backup_files = Do not delete old backup files
  label.keep_only_this_number_of_backup_files = Keep only this number of most recent backup files
- label.autodelete_old_backup_files = Autodelete old backup files:
+ label.autodelete_old_backup_files = Auto-delete old backup files:
  label.always_ask = Always ask
  label.auto_delete = Automatically delete
  label.filename = filename
@@@ -1374,10 -1370,18 +1375,18 @@@ label.configuration = Configuratio
  label.configure_feature_tooltip = Click to configure variable colour or filters
  label.schemes = Schemes
  label.customise = Customise
+ label.custom = Custom
  label.default = Default
  label.single_file = Single backup
  label.keep_all_versions = Keep all versions
  label.rolled_backups = Rolled backup files
+ label.customise_description = Select Customise, make changes, and click on OK to save your own custom scheme
+ label.custom_description = Your own saved scheme
+ label.default_description = Keep the last three versions of the file
+ label.single_file_description = Keep the last version of the file
+ label.keep_all_versions_description = Keep all previous versions of the file
+ label.rolled_backups_description = Keep the last nine versions of the file from _bak.1 (newest) to _bak.9 (oldest)
+ label.cancel_changes_description = Cancel changes made to your last saved Custom scheme
  label.previously_saved_scheme = Previously saved scheme
  label.no_backup_files = NO BACKUP FILES
  label.include_backup_files = Include backup files
@@@ -726,8 -726,7 +726,8 @@@ label.services_at = Servicios en {0
  label.rest_client_submit = {0} utilizando {1}
  label.fetch_retrieve_from =Recuperar de {0}
  label.fetch_retrieve_from_all_sources = Recuperar de todas las fuentes {0} en {1}<br>La primera es :{2}
 -label.feature_settings_click_drag = Haga clic o arrastre los tipos de las características hacia arriba o hacia abajo para cambiar el orden de visualización.<br/>Haga doble clic para seleccionar las columnas que contienen las características del alineamiento/selección actual.<br/>
 +label.feature_settings_click_drag = Haga clic o arrastre los tipos de las características hacia arriba o hacia abajo para cambiar el orden de visualización.<br/>Haga doble clic para seleccionar las columnas que contienen las características del alineamiento/selección actual.
 +label.feature_settings_select_columns =Haga doble clic para seleccionar las columnas que contienen las características del alineamiento/selección actual
  label.opt_and_params_further_details = ver los detalles adicionales haciendo clic en el botón derecho
  label.opt_and_params_show_brief_desc_image_link = Haga clic para ver una descripción breve<br><img src="{0}"/>Haga clic en el botón derecho para obtener información adicional.
  label.opt_and_params_show_brief_desc = Haga clic para ver una descripción breve<br>
@@@ -1341,10 -1340,6 +1341,10 @@@ label.most_bound_molecules = Más Molécu
  label.most_polymer_residues = Más Residuos de Polímeros
  label.cached_structures = Estructuras en Caché
  label.free_text_search = Búsqueda de texto libre
 +label.summary_view = Vista Resumida
 +label.group_by_so = Agrupar por términos de la Sequence Ontology
 +label.apply_to_subtypes = Aplicar también a características y subtipos de
 +label.apply_also_to = Aplicar también a:
  label.backupfiles_confirm_delete = Confirmar borrar
  label.backupfiles_confirm_delete_old_files = ¿Borrar los siguientes archivos? (ver la pestaña 'Copias' de la ventana de Preferencias para más opciones)
  label.backupfiles_confirm_save_file = Confirmar guardar archivo
@@@ -1360,6 -1355,7 +1360,7 @@@ label.append_to_filename = Adjuntar tex
  label.append_to_filename_tooltip = %n en el texto será reemplazado por el número de respaldo. El texto será después del nombre del archivo. Vea el cuadro de resumen arriba.
  label.index_digits = Número de dígitos a utilizar para el número de respaldo.
  label.summary_of_backups_scheme = Resumen del esquema de copias de seguridad
+ label.scheme_examples = Ejemplos de esquema
  label.increment_index = Aumente los números de texto adjuntos: el archivo más nuevo tiene el número más grande
  label.reverse_roll = Ciclos de texto adjuntos: el respaldo más reciente es siempre el número 1
  label.keep_files = Borrando los respaldos antiguos
@@@ -1374,11 -1370,19 +1375,19 @@@ label.braced_newest = (mas nuevo
  label.configuration = Configuración
  label.configure_feature_tooltip = Haga clic para configurar el color o los filtros
  label.schemes = Esquemas
- label.customise = Personalizado
+ label.customise = Personalizar
+ label.custom = Personal
  label.default = Defecto
  label.single_file = Solo uno respaldo
  label.keep_all_versions = Mantener todas las versiones
  label.rolled_backups = Ciclos respaldos
+ label.customise_description = Seleccione Personalizar, haga cambios y haga clic en OK para guardar su propio esquema personalizado
+ label.custom_description = Tu propio esquema guardado
+ label.default_description = Conserve las últimas tres versiones del archivo
+ label.single_file_description = Conserve la última versión del archivo
+ label.keep_all_versions_description = Mantener todas las versiones anteriores del archivo
+ label.rolled_backups_description = Mantenga las últimas nueve versiones del archivo desde _bak.1 (más reciente) a _bak.9 (más antigua)
+ label.cancel_changes_description = Cancelar los cambios realizados en su último esquema personalizado guardado
  label.previously_saved_scheme = Esquema previamente guardado
  label.no_backup_files = NO ARCHIVOS DE RESPALDOS
  label.include_backup_files = Incluir archivos de respaldos
@@@ -345,7 -345,7 +345,7 @@@ public class Jalvie
       * configure 'full' SO model if preferences say to, 
       * else use the default (SO Lite)
       */
 -    if (Cache.getDefault("USE_FULL_SO", false))
 +    if (Cache.getDefault("USE_FULL_SO", true))
      {
        SequenceOntologyFactory.setInstance(new SequenceOntology());
      }
          JalviewTaskbar.setTaskbar(this);
        } catch (Exception e)
        {
-         e.printStackTrace();
+         System.out.println("Cannot set Taskbar");
+         // e.printStackTrace();
        } catch (Throwable t)
        {
-         t.printStackTrace();
+         System.out.println("Cannot set Taskbar");
+         // t.printStackTrace();
        }
  
        desktop.setVisible(true);
@@@ -1203,23 -1203,7 +1203,7 @@@ public class AlignFrame extends GAlignF
            PrintWriter out = new PrintWriter(
                    new FileWriter(backupfiles.getTempFilePath()));
  
-           // TESTING code here
-           boolean TESTING = true;
-           if (TESTING)
-           {
-             out.print("; TESTSTART\n");
-             int count = 20;
-             for (int i = 0; i < count; i++)
-             {
-               // Thread.sleep(1000);
-               out.println("; TEST: " + (count - 1 - i));
-             }
-           }
            out.print(output);
-           if (TESTING)
-           {
-             out.print("; TESTEND\n");
-           }
            out.close();
            this.setTitle(file);
            statusBar.setText(MessageManager.formatMessage(
    }
  
    /**
 -   * Hides columns containing (or not containing) a specified feature, provided
 -   * that would not leave all columns hidden
 +   * Hides columns containing (or not containing) the specified feature(s),
 +   * provided that would not leave all columns hidden
     * 
 -   * @param featureType
     * @param columnsContaining
 +   * @param featureTypes
 +   * 
     * @return
     */
 -  public boolean hideFeatureColumns(String featureType,
 -          boolean columnsContaining)
 +  public boolean hideFeatureColumns(boolean columnsContaining,
 +          String... featureTypes)
    {
      boolean notForHiding = avc.markColumnsContainingFeatures(
 -            columnsContaining, false, false, featureType);
 +            columnsContaining, false, false, featureTypes);
      if (notForHiding)
      {
        if (avc.markColumnsContainingFeatures(!columnsContaining, false,
 -              false, featureType))
 +              false, featureTypes))
        {
          getViewport().hideSelectedColumns();
          return true;
@@@ -22,6 -22,7 +22,7 @@@ package jalview.gui
  
  import jalview.api.AlignmentViewPanel;
  import jalview.api.FeatureColourI;
+ import jalview.bin.Cache;
  import jalview.datamodel.GraphLine;
  import jalview.datamodel.features.FeatureAttributes;
  import jalview.datamodel.features.FeatureAttributes.Datatype;
@@@ -29,8 -30,6 +30,8 @@@ import jalview.datamodel.features.Featu
  import jalview.datamodel.features.FeatureMatcherI;
  import jalview.datamodel.features.FeatureMatcherSet;
  import jalview.datamodel.features.FeatureMatcherSetI;
 +import jalview.io.gff.SequenceOntologyFactory;
 +import jalview.io.gff.SequenceOntologyI;
  import jalview.schemes.FeatureColour;
  import jalview.util.ColorUtils;
  import jalview.util.MessageManager;
@@@ -51,12 -50,7 +52,12 @@@ import java.awt.event.MouseAdapter
  import java.awt.event.MouseEvent;
  import java.text.DecimalFormat;
  import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.Comparator;
 +import java.util.HashMap;
  import java.util.List;
 +import java.util.Map;
 +import java.util.Map.Entry;
  
  import javax.swing.BorderFactory;
  import javax.swing.BoxLayout;
@@@ -124,11 -118,10 +125,11 @@@ public class FeatureTypeSettings extend
  
    /*
     * the colour and filters to reset to on Cancel
 +   * (including feature sub-types if modified)
     */
 -  private final FeatureColourI originalColour;
 +  private Map<String, FeatureColourI> originalColours;
  
 -  private final FeatureMatcherSetI originalFilter;
 +  private Map<String, FeatureMatcherSetI> originalFilters;
  
    /*
     * set flag to true when setting values programmatically,
  
    private JPanel maxColour = new JPanel();
  
-   private JComboBox<String> threshold = new JComboBox<>();
+   private JComboBox<Object> threshold = new JComboBox<>();
  
    private JSlider slider = new JSlider();
  
    /*
     * choice of option for 'colour for no value'
     */
-   private JComboBox<String> noValueCombo;
+   private JComboBox<Object> noValueCombo;
  
    /*
     * choice of what to colour by text (Label or attribute)
     */
-   private JComboBox<String> colourByTextCombo;
+   private JComboBox<Object> colourByTextCombo;
  
    /*
     * choice of what to colour by range (Score or attribute)
     */
-   private JComboBox<String> colourByRangeCombo;
+   private JComboBox<Object> colourByRangeCombo;
  
    private JRadioButton andFilters;
  
  
    private JPanel chooseFiltersPanel;
  
 +  /*
 +   * the root Sequence Ontology terms (if any) that is a parent of
 +   * the current feature type
 +   */
 +  private String rootSOTerm;
 +
 +  /*
 +   * a map whose keys are Sequence Ontology terms - selected from the
 +   * current term and its parents in the SO - whose subterms include
 +   * additional feature types; the map entry is the list of additional
 +   * feature types that match the key or have it as a parent term; in
 +   * other words, distinct 'aggregations' that include the current feature type
 +   */
 +  private final Map<String, List<String>> relatedSoTerms;
 +
 +  /*
 +   * if true, filter or colour settings are also applied to 
 +   * any sub-types of parentTerm in the Sequence Ontology
 +   */
 +  private boolean applyFiltersToSubtypes;
 +
 +  private boolean applyColourToSubtypes;
 +
 +  private String parentSOTerm;
 +
    /**
     * Constructor
     * 
      this.fr = frender;
      this.featureType = theType;
      ap = fr.ap;
 -    originalFilter = fr.getFeatureFilter(theType);
 -    originalColour = fr.getFeatureColours().get(theType);
 -    
 +
 +    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
 +    relatedSoTerms = so.findSequenceOntologyGroupings(
 +            this.featureType, fr.getRenderOrder());
 +
 +    /*
 +     * save original colours and filters for this feature type,
 +     * and any related types, to restore on Cancel
 +     */
 +    originalFilters = new HashMap<>();
 +    originalFilters.put(theType, fr.getFeatureFilter(theType));
 +    originalColours = new HashMap<>();
 +    originalColours.put(theType, fr.getFeatureColours().get(theType));
 +    for (List<String> related : relatedSoTerms.values())
 +    {
 +      for (String type : related)
 +      {
 +        originalFilters.put(type, fr.getFeatureFilter(type));
 +        originalColours.put(type, fr.getFeatureColours().get(type));
 +      }
 +    }
 +
      adjusting = true;
 -    
 +
      try
      {
        initialise();
        ex.printStackTrace();
        return;
      }
 -    
 +
      updateColoursTab();
 -    
 +
      updateFiltersTab();
 -    
 +
      adjusting = false;
 -    
 +
      colourChanged(false);
 -    
 +
      String title = MessageManager
              .formatMessage("label.display_settings_for", new String[]
              { theType });
    }
  
    /**
 +   * Answers a (possibly empty) map of any Sequence Ontology terms (the current
 +   * feature type and its parents) which incorporate additional known feature
 +   * types (the map entry).
 +   * <p>
 +   * For example if {@code stop_gained} and {@code stop_lost} are known feature
 +   * types, then SO term {@ nonsynonymous_variant} is the first common parent of
 +   * both terms
 +   * 
 +   * @param featureType
 +   *          the current feature type being configured
 +   * @param featureTypes
 +   *          all known feature types on the alignment
 +   * @return
 +   */
 +  protected static Map<String, List<String>> findSequenceOntologyGroupings(
 +          String featureType, List<String> featureTypes)
 +  {
 +    List<String> sortedTypes = new ArrayList<>(featureTypes);
 +    Collections.sort(sortedTypes);
 +
 +    Map<String, List<String>> parents = new HashMap<>();
 +
 +    /*
 +     * method: 
 +     * walk up featureType and all of its parents
 +     * find other feature types which are subsumed by each term
 +     * add each distinct aggregation of included feature types to the map
 +     */
 +    List<String> candidates = new ArrayList<>();
 +    SequenceOntologyI so = SequenceOntologyFactory.getInstance();
 +    candidates.add(featureType);
 +    while (!candidates.isEmpty())
 +    {
 +      String term = candidates.remove(0);
 +      List<String> includedFeatures = new ArrayList<>();
 +      for (String type : sortedTypes)
 +      {
 +        if (!type.equals(featureType) && so.isA(type, term))
 +        {
 +          includedFeatures.add(type);
 +        }
 +      }
 +      if (!includedFeatures.isEmpty()
 +              && !parents.containsValue(includedFeatures))
 +      {
 +        parents.put(term, includedFeatures);
 +      }
 +      candidates.addAll(so.getParents(term));
 +    }
 +    
 +    return parents;
 +  }
 +
 +  /**
     * Configures the widgets on the Colours tab according to the current feature
     * colour scheme
     */
       * if not set, default max colour to last plain colour,
       * and make min colour a pale version of max colour
       */
 +    FeatureColourI originalColour = originalColours.get(featureType);
      Color max = originalColour.getMaxColour();
      if (max == null)
      {
              MessageManager.getString("action.colour"), true);
  
      /*
 +     * option to apply colour to other selected types as well
 +     */
 +    if (!relatedSoTerms.isEmpty())
 +    {
 +      applyColourToSubtypes = false;
 +      colourByPanel.add(initSubtypesPanel(false));
 +    }
 +
 +    /*
       * simple colour radio button and colour picker
       */
      JPanel simpleColourPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
      singleColour.setFont(JvSwingUtils.getLabelFont());
      singleColour.setBorder(BorderFactory.createLineBorder(Color.black));
      singleColour.setPreferredSize(new Dimension(40, 20));
 -    // if (originalColour.isGraduatedColour())
 -    // {
 -    // singleColour.setBackground(originalColour.getMaxColour());
 -    // singleColour.setForeground(originalColour.getMaxColour());
 -    // }
 -    // else
 -    // {
 -      singleColour.setBackground(originalColour.getColour());
 -      singleColour.setForeground(originalColour.getColour());
 -    // }
 +    FeatureColourI originalColour = originalColours.get(featureType);
 +    singleColour.setBackground(originalColour.getColour());
 +    singleColour.setForeground(originalColour.getColour());
 +
      singleColour.addMouseListener(new MouseAdapter()
      {
        @Override
      return colourByPanel;
    }
  
 +  /**
 +   * Constructs and returns a panel with the option to apply any changes also to
 +   * sub-types of SO terms at or above the feature type
 +   * 
 +   * @return
 +   */
 +  protected JPanel initSubtypesPanel(final boolean forFilters)
 +  {
 +    JPanel toSubtypes = new JPanel(new FlowLayout(FlowLayout.LEFT));
 +    toSubtypes.setBackground(Color.WHITE);
 +
 +    /*
 +     * checkbox 'apply to sub-types of...'
 +     */
 +    JCheckBox applyToSubtypesCB = new JCheckBox(MessageManager
 +            .formatMessage("label.apply_to_subtypes", rootSOTerm));
 +    toSubtypes.add(applyToSubtypesCB);
 +    toSubtypes
 +            .setToolTipText(MessageManager.getString("label.group_by_so"));
 +
 +    /*
 +     * combobox to choose 'parent' of sub-types
 +     */
 +    List<String> soTerms = new ArrayList<>();
 +    for (String term : relatedSoTerms.keySet())
 +    {
 +      soTerms.add(term);
 +    }
 +    // sort from most restrictive to most inclusive
 +    Collections.sort(soTerms, new Comparator<String>()
 +    {
 +      @Override
 +      public int compare(String o1, String o2)
 +      {
 +        return Integer.compare(relatedSoTerms.get(o1).size(),
 +                relatedSoTerms.get(o2).size());
 +      }
 +    });
 +    List<String> tooltips = new ArrayList<>();
 +    for (String term : soTerms)
 +    {
 +      tooltips.add(getSOTermsTooltip(relatedSoTerms.get(term)));
 +    }
 +    JComboBox<String> parentType = JvSwingUtils
 +            .buildComboWithTooltips(soTerms, tooltips);
 +    toSubtypes.add(parentType);
 +
 +    /*
 +     * on toggle of checkbox, or change of parent SO term,
 +     * reset and then reapply filters to the selected scope
 +     */
 +    final ActionListener action = new ActionListener()
 +    {
 +      /*
 +       * reset and reapply settings on toggle of checkbox
 +       */
 +      @Override
 +      public void actionPerformed(ActionEvent e)
 +      {
 +        parentSOTerm = (String) parentType.getSelectedItem();
 +        if (forFilters)
 +        {
 +          applyFiltersToSubtypes = applyToSubtypesCB.isSelected();
 +          restoreOriginalFilters();
 +          filtersChanged();
 +        }
 +        else
 +        {
 +          applyColourToSubtypes = applyToSubtypesCB.isSelected();
 +          restoreOriginalColours();
 +          colourChanged(true);
 +        }
 +      }
 +    };
 +    applyToSubtypesCB.addActionListener(action);
 +    parentType.addActionListener(action);
 +
 +    return toSubtypes;
 +  }
 +
    private void showColourChooser(JPanel colourPanel, String key)
    {
      Color col = JColorChooser.showDialog(this,
      FeatureColourI acg = makeColourFromInputs();
  
      /*
 -     * save the colour, and repaint stuff
 +     * save the colour, and set on subtypes if selected
       */
      fr.setColour(featureType, acg);
 +    if (applyColourToSubtypes)
 +    {
 +      for (String child : relatedSoTerms.get(parentSOTerm))
 +      {
 +        fr.setColour(child, acg);
 +      }
 +    }
 +    refreshFeatureSettings();
      ap.paintAlignment(updateStructsAndOverview, updateStructsAndOverview);
  
      updateColoursTab();
    @Override
    protected void raiseClosed()
    {
 +    refreshFeatureSettings();
 +  }
 +
 +  protected void refreshFeatureSettings()
 +  {
      if (this.featureSettings != null)
      {
 -      featureSettings.actionPerformed(new ActionEvent(this, 0, "CLOSED"));
 +      featureSettings.actionPerformed(new ActionEvent(this, 0, "REFRESH"));
      }
    }
  
  
    /**
     * Action on Cancel is to restore colour scheme and filters as they were when
 -   * the dialog was opened
 +   * the dialog was opened (including any feature sub-types that may have been
 +   * changed)
     */
    @Override
    public void cancelPressed()
    {
 -    fr.setColour(featureType, originalColour);
 -    fr.setFeatureFilter(featureType, originalFilter);
 +    restoreOriginalColours();
 +    restoreOriginalFilters();
      ap.paintAlignment(true, true);
    }
  
    /**
 +   * Restores filters for all feature types to their values when the dialog was
 +   * opened
 +   */
 +  protected void restoreOriginalFilters()
 +  {
 +    for (Entry<String, FeatureMatcherSetI> entry : originalFilters
 +            .entrySet())
 +    {
 +      fr.setFeatureFilter(entry.getKey(), entry.getValue());
 +    }
 +  }
 +
 +  /**
 +   * Restores colours for all feature types to their values when the dialog was
 +   * opened
 +   */
 +  protected void restoreOriginalColours()
 +  {
 +    for (Entry<String, FeatureColourI> entry : originalColours.entrySet())
 +    {
 +      fr.setColour(entry.getKey(), entry.getValue());
 +    }
 +  }
 +
 +  /**
     * Action on text entry of a threshold value
     */
    protected void thresholdValue_actionPerformed()
         */
        adjusting = true;
        float f = Float.parseFloat(thresholdValue.getText());
 -      f = Float.max(f,  this.min);
 +      f = Float.max(f, this.min);
        f = Float.min(f, this.max);
        thresholdValue.setText(String.valueOf(f));
        slider.setValue((int) (f * scaleFactor));
     * @param withRange
     * @param withText
     */
-   protected JComboBox<String> populateAttributesDropdown(
+   protected JComboBox<Object> populateAttributesDropdown(
            List<String[]> attNames, boolean withRange, boolean withText)
    {
      List<String> displayAtts = new ArrayList<>();
        tooltips.add(desc == null ? "" : desc);
      }
  
-     JComboBox<String> attCombo = JvSwingUtils
-             .buildComboWithTooltips(displayAtts, tooltips);
+     // now convert String List to Object List for buildComboWithTooltips
+     List<Object> displayAttsObjects = new ArrayList<>(displayAtts);
+     JComboBox<Object> attCombo = JvSwingUtils
+             .buildComboWithTooltips(displayAttsObjects, tooltips);
+     
      return attCombo;
    }
  
    {
      filters = new ArrayList<>();
  
 +    JPanel outerPanel = new JPanel();
 +    outerPanel.setLayout(new BoxLayout(outerPanel, BoxLayout.Y_AXIS));
 +    outerPanel.setBackground(Color.white);
 +
 +    /*
 +     * option to apply colour to other selected types as well
 +     */
 +    if (!relatedSoTerms.isEmpty())
 +    {
 +      applyFiltersToSubtypes = false;
 +      outerPanel.add(initSubtypesPanel(true));
 +    }
 +
      JPanel filtersPanel = new JPanel();
      filtersPanel.setLayout(new BoxLayout(filtersPanel, BoxLayout.Y_AXIS));
      filtersPanel.setBackground(Color.white);
      JvSwingUtils.createTitledBorder(filtersPanel,
              MessageManager.getString("label.filters"), true);
 +    outerPanel.add(filtersPanel);
  
      JPanel andOrPanel = initialiseAndOrPanel();
      filtersPanel.add(andOrPanel);
      chooseFiltersPanel.setBackground(Color.white);
      filtersPanel.add(chooseFiltersPanel);
  
 -    return filtersPanel;
 +    return outerPanel;
    }
  
    /**
     */
    private JPanel initialiseAndOrPanel()
    {
 -    JPanel andOrPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
 +    JPanel andOrPanel = new JPanel(new BorderLayout());
      andOrPanel.setBackground(Color.white);
 +
      andFilters = new JRadioButton(MessageManager.getString("label.and"));
      orFilters = new JRadioButton(MessageManager.getString("label.or"));
      ActionListener actionListener = new ActionListener()
              new JLabel(MessageManager.getString("label.join_conditions")));
      andOrPanel.add(andFilters);
      andOrPanel.add(orFilters);
 +
      return andOrPanel;
    }
  
    /**
 +   * Builds a tooltip for the 'Apply also to...' combobox with a list of known
 +   * feature types (excluding the current type) which are sub-types of the
 +   * selected Sequence Ontology term
 +   * 
 +   * @param
 +   * @return
 +   */
 +  protected String getSOTermsTooltip(List<String> list)
 +  {
 +    StringBuilder sb = new StringBuilder(20 * relatedSoTerms.size());
 +    sb.append(MessageManager.getString("label.apply_also_to"));
 +    for (String child : list)
 +    {
 +      sb.append("<br>").append(child);
 +    }
 +    String tooltip = JvSwingUtils.wrapTooltip(true, sb.toString());
 +    return tooltip;
 +  }
 +
 +  /**
     * Refreshes the display to show any filters currently configured for the
     * selected feature type (editable, with 'remove' option), plus one extra row
     * for adding a condition. This should be called after a filter has been
       * drop-down choice of attribute, with description as a tooltip 
       * if we can obtain it
       */
-     final JComboBox<String> attCombo = populateAttributesDropdown(attNames,
+     final JComboBox<Object> attCombo = populateAttributesDropdown(attNames,
              true, true);
      String filterBy = setSelectedAttribute(attCombo, filter);
  
     * @param attCombo
     * @param filter
     */
-   private String setSelectedAttribute(JComboBox<String> attCombo,
+   private String setSelectedAttribute(JComboBox<Object> attCombo,
            FeatureMatcherI filter)
    {
      String item = null;
     * @param valueField
     * @param filterIndex
     */
-   protected boolean updateFilter(JComboBox<String> attCombo,
+   protected boolean updateFilter(JComboBox<Object> attCombo,
            JComboBox<Condition> condCombo, JTextField valueField,
            int filterIndex)
    {
-     String attName = (String) attCombo.getSelectedItem();
+     String attName;
+     try
+     {
+       attName = (String) attCombo.getSelectedItem();
+     } catch (Exception e)
+     {
+       Cache.log.error("Problem casting Combo box entry to String");
+       attName = attCombo.getSelectedItem().toString();
+     }
      Condition cond = (Condition) condCombo.getSelectedItem();
      String pattern = valueField.getText().trim();
  
       * (note this might now be an empty filter with no conditions)
       */
      fr.setFeatureFilter(featureType, combined.isEmpty() ? null : combined);
 +    if (applyFiltersToSubtypes)
 +    {
 +      for (String child : relatedSoTerms.get(parentSOTerm))
 +      {
 +        fr.setFeatureFilter(child, combined.isEmpty() ? null : combined);
 +      }
 +    }
 +
 +    refreshFeatureSettings();
      ap.paintAlignment(true, true);
  
      updateFiltersTab();