X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FFeatureSettings.java;h=45b6b0d850b50fa485f94667ba1743183837b1ea;hb=136c0793b90b72b928c4d77dc109dd5c644e00d3;hp=bb5f13cecec2927c33881da55ce2ecc409676eb1;hpb=8677e6e34e291edc58c1da2fc9c958473754143f;p=jalview.git diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java index bb5f13c..45b6b0d 100644 --- a/src/jalview/gui/FeatureSettings.java +++ b/src/jalview/gui/FeatureSettings.java @@ -23,7 +23,7 @@ package jalview.gui; import jalview.api.FeatureColourI; import jalview.api.FeatureSettingsControllerI; import jalview.bin.Cache; -import jalview.datamodel.SequenceFeature; +import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; import jalview.gui.Help.HelpId; import jalview.io.JalviewFileChooser; @@ -40,6 +40,7 @@ import jalview.ws.dbsources.das.api.jalviewSourceI; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; +import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.GridLayout; @@ -60,6 +61,7 @@ import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.Arrays; +import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; @@ -126,6 +128,15 @@ public class FeatureSettings extends JPanel implements JPanel transPanel = new JPanel(new GridLayout(1, 2)); + private static final int MIN_WIDTH = 400; + + private static final int MIN_HEIGHT = 400; + + /** + * Constructor + * + * @param af + */ public FeatureSettings(AlignFrame af) { this.af = af; @@ -278,6 +289,7 @@ public class FeatureSettings extends JPanel implements MessageManager.getString("label.sequence_feature_settings"), 400, 450); } + frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT)); frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter() { @@ -463,50 +475,26 @@ public class FeatureSettings extends JPanel implements private boolean handlingUpdate = false; /** - * contains a float[3] for each feature type string. created by setTableData + * holds {featureCount, totalExtent} for each feature type */ Map typeWidth = null; @Override synchronized public void discoverAllFeatureData() { - Vector allFeatures = new Vector(); - Vector allGroups = new Vector(); - SequenceFeature[] tmpfeatures; - String group; - for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++) - { - tmpfeatures = af.getViewport().getAlignment().getSequenceAt(i) - .getSequenceFeatures(); - if (tmpfeatures == null) - { - continue; - } + Set allGroups = new HashSet(); + AlignmentI alignment = af.getViewport().getAlignment(); - int index = 0; - while (index < tmpfeatures.length) + for (int i = 0; i < alignment.getHeight(); i++) + { + SequenceI seq = alignment.getSequenceAt(i); + for (String group : seq.getFeatures().getFeatureGroups(true)) { - if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0) - { - index++; - continue; - } - - if (tmpfeatures[index].getFeatureGroup() != null) - { - group = tmpfeatures[index].featureGroup; - if (!allGroups.contains(group)) - { - allGroups.addElement(group); - checkGroupState(group); - } - } - - if (!allFeatures.contains(tmpfeatures[index].getType())) + if (group != null && !allGroups.contains(group)) { - allFeatures.addElement(tmpfeatures[index].getType()); + allGroups.add(group); + checkGroupState(group); } - index++; } } @@ -525,27 +513,15 @@ public class FeatureSettings extends JPanel implements { boolean visible = fr.checkGroupVisibility(group, true); - if (groupPanel == null) - { - groupPanel = new JPanel(); - } - - boolean alreadyAdded = false; for (int g = 0; g < groupPanel.getComponentCount(); g++) { if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group)) { - alreadyAdded = true; ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible); - break; + return visible; } } - if (alreadyAdded) - { - - return visible; - } final String grp = group; final JCheckBox check = new JCheckBox(group, visible); check.setFont(new Font("Serif", Font.BOLD, 12)); @@ -572,7 +548,7 @@ public class FeatureSettings extends JPanel implements synchronized void resetTable(String[] groupChanged) { - if (resettingTable == true) + if (resettingTable) { return; } @@ -580,69 +556,59 @@ public class FeatureSettings extends JPanel implements typeWidth = new Hashtable(); // TODO: change avWidth calculation to 'per-sequence' average and use long // rather than float - float[] avWidth = null; - SequenceFeature[] tmpfeatures; - String group = null, type; - Vector visibleChecks = new Vector(); - - // Find out which features should be visible depending on which groups - // are selected / deselected - // and recompute average width ordering + + Set displayableTypes = new HashSet(); + Set foundGroups = new HashSet(); + + /* + * 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 groups = seq.getFeatures().getFeatureGroups(true); + Set visibleGroups = new HashSet(); + for (String group : groups) { - group = tmpfeatures[index].featureGroup; - - 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]); - } - else - { - avWidth = typeWidth.get(tmpfeatures[index].getType()); + visibleGroups.add(group); } - 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 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); } } - int fSize = visibleChecks.size(); - Object[][] data = new Object[fSize][3]; + Object[][] data = new Object[displayableTypes.size()][3]; int dataIndex = 0; if (fr.hasRenderOrder()) @@ -658,9 +624,9 @@ public class FeatureSettings extends JPanel implements List frl = fr.getRenderOrder(); for (int ro = frl.size() - 1; ro > -1; ro--) { - type = frl.get(ro); + String type = frl.get(ro); - if (!visibleChecks.contains(type)) + if (!displayableTypes.contains(type)) { continue; } @@ -670,16 +636,17 @@ public class FeatureSettings extends JPanel implements data[dataIndex][2] = new Boolean(af.getViewport() .getFeaturesDisplayed().isVisible(type)); dataIndex++; - visibleChecks.removeElement(type); + displayableTypes.remove(type); } } - fSize = visibleChecks.size(); - for (int i = 0; i < fSize; i++) + /* + * process any extra features belonging only to + * a group which was just selected + */ + while (!displayableTypes.isEmpty()) { - // These must be extra features belonging to the group - // which was just selected - type = visibleChecks.elementAt(i).toString(); + String type = displayableTypes.iterator().next(); data[dataIndex][0] = type; data[dataIndex][1] = fr.getFeatureStyle(type); @@ -692,6 +659,7 @@ public class FeatureSettings extends JPanel implements data[dataIndex][2] = new Boolean(true); dataIndex++; + displayableTypes.remove(type); } if (originalData == null) @@ -702,24 +670,105 @@ public class FeatureSettings extends JPanel implements System.arraycopy(data[i], 0, originalData[i], 0, 3); } } + else + { + updateOriginalData(data); + } table.setModel(new FeatureTableModel(data)); table.getColumnModel().getColumn(0).setPreferredWidth(200); - if (groupPanel != null) - { - groupPanel.setLayout(new GridLayout( - fr.getFeatureGroupsSize() / 4 + 1, 4)); - - groupPanel.validate(); - bigPanel.add(groupPanel, BorderLayout.NORTH); - } + groupPanel.setLayout(new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, + 4)); + pruneGroups(foundGroups); + groupPanel.validate(); updateFeatureRenderer(data, groupChanged != null); resettingTable = false; } /** + * Updates 'originalData' (used for restore on Cancel) if we detect that + * changes have been made outwith this dialog + *
    + *
  • a new feature type added (and made visible)
  • + *
  • a feature colour changed (in the Amend Features dialog)
  • + *
+ * + * @param foundData + */ + protected void updateOriginalData(Object[][] foundData) + { + // todo LinkedHashMap instead of Object[][] would be nice + + Object[][] currentData = ((FeatureTableModel) table.getModel()) + .getData(); + for (Object[] row : foundData) + { + String type = (String) row[0]; + boolean found = false; + for (Object[] current : currentData) + { + if (type.equals(current[0])) + { + found = true; + /* + * currently dependent on object equality here; + * really need an equals method on FeatureColour + */ + if (!row[1].equals(current[1])) + { + /* + * feature colour has changed externally - update originalData + */ + for (Object[] original : originalData) + { + if (type.equals(original[0])) + { + original[1] = row[1]; + break; + } + } + } + break; + } + } + if (!found) + { + /* + * new feature detected - add to original data (on top) + */ + Object[][] newData = new Object[originalData.length + 1][3]; + for (int i = 0; i < originalData.length; i++) + { + System.arraycopy(originalData[i], 0, newData[i + 1], 0, 3); + } + newData[0] = row; + originalData = newData; + } + } + } + + /** + * Remove from the groups panel any checkboxes for groups that are not in the + * foundGroups set. This enables removing a group from the display when the + * last feature in that group is deleted. + * + * @param foundGroups + */ + protected void pruneGroups(Set foundGroups) + { + for (int g = 0; g < groupPanel.getComponentCount(); g++) + { + JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g); + if (!foundGroups.contains(checkbox.getText())) + { + groupPanel.remove(checkbox); + } + } + } + + /** * reorder data based on the featureRenderers global priority list. * * @param data @@ -748,8 +797,7 @@ public class FeatureSettings extends JPanel implements void load() { - JalviewFileChooser chooser = new JalviewFileChooser( - Cache.getProperty("LAST_DIRECTORY"), "fc", + JalviewFileChooser chooser = new JalviewFileChooser("fc", "Sequence Feature Colours"); chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle(MessageManager @@ -841,8 +889,7 @@ public class FeatureSettings extends JPanel implements void save() { - JalviewFileChooser chooser = new JalviewFileChooser( - Cache.getProperty("LAST_DIRECTORY"), "fc", + JalviewFileChooser chooser = new JalviewFileChooser("fc", "Sequence Feature Colours"); chooser.setFileView(new JalviewFileView()); chooser.setDialogTitle(MessageManager @@ -1061,6 +1108,10 @@ public class FeatureSettings extends JPanel implements settingsPane.setLayout(borderLayout2); dasSettingsPane.setLayout(borderLayout3); bigPanel.setLayout(borderLayout4); + + groupPanel = new JPanel(); + bigPanel.add(groupPanel, BorderLayout.NORTH); + invert.setFont(JvSwingUtils.getLabelFont()); invert.setText(MessageManager.getString("label.invert_selection")); invert.addActionListener(new ActionListener()