X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FFeatureSettings.java;h=63f1b137853a8af84f0f6133616d324222623264;hb=ad79fcf0705b71f2504b1ed4092d54a8c45ef09f;hp=feb09fca9697e6a62e1eceea6ebe93e3db0c3b82;hpb=b59839dfda0a767318fc3c89d9604c39ff298d83;p=jalview.git diff --git a/src/jalview/gui/FeatureSettings.java b/src/jalview/gui/FeatureSettings.java index feb09fc..63f1b13 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; @@ -61,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; @@ -96,8 +97,8 @@ import javax.swing.table.AbstractTableModel; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; -public class FeatureSettings extends JPanel implements - FeatureSettingsControllerI +public class FeatureSettings extends JPanel + implements FeatureSettingsControllerI { DasSourceBrowser dassourceBrowser; @@ -130,14 +131,24 @@ public class FeatureSettings extends JPanel implements private static final int MIN_WIDTH = 400; private static final int MIN_HEIGHT = 400; + + /** + * when true, constructor is still executing - so ignore UI events + */ + protected volatile boolean inConstruction = true; + /** + * Constructor + * + * @param af + */ public FeatureSettings(AlignFrame af) { this.af = af; fr = af.getFeatureRenderer(); // allow transparency to be recovered - transparency.setMaximum(100 - (int) ((originalTransparency = fr - .getTransparency()) * 100)); + transparency.setMaximum(100 + - (int) ((originalTransparency = fr.getTransparency()) * 100)); try { @@ -285,17 +296,19 @@ public class FeatureSettings extends JPanel implements } frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT)); - frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter() - { - @Override - public void internalFrameClosed( - javax.swing.event.InternalFrameEvent evt) - { - fr.removePropertyChangeListener(change); - dassourceBrowser.fs = null; - }; - }); + frame.addInternalFrameListener( + new javax.swing.event.InternalFrameAdapter() + { + @Override + public void internalFrameClosed( + javax.swing.event.InternalFrameEvent evt) + { + fr.removePropertyChangeListener(change); + dassourceBrowser.fs = null; + }; + }); frame.setLayer(JLayeredPane.PALETTE_LAYER); + inConstruction = false; } protected void popupSort(final int selectedRow, final String type, @@ -304,8 +317,9 @@ public class FeatureSettings extends JPanel implements { final FeatureColourI featureColour = (FeatureColourI) typeCol; - JPopupMenu men = new JPopupMenu(MessageManager.formatMessage( - "label.settings_for_param", new String[] { type })); + JPopupMenu men = new JPopupMenu(MessageManager + .formatMessage("label.settings_for_param", new String[] + { type })); JMenuItem scr = new JMenuItem( MessageManager.getString("label.sort_by_score")); men.add(scr); @@ -316,8 +330,9 @@ public class FeatureSettings extends JPanel implements @Override public void actionPerformed(ActionEvent e) { - me.af.avc.sortAlignmentByFeatureScore(Arrays - .asList(new String[] { type })); + me.af.avc + .sortAlignmentByFeatureScore(Arrays.asList(new String[] + { type })); } }); @@ -329,8 +344,9 @@ public class FeatureSettings extends JPanel implements @Override public void actionPerformed(ActionEvent e) { - me.af.avc.sortAlignmentByFeatureDensity(Arrays - .asList(new String[] { type })); + me.af.avc + .sortAlignmentByFeatureDensity(Arrays.asList(new String[] + { type })); } }); @@ -400,8 +416,7 @@ public class FeatureSettings extends JPanel implements else { // probably the color chooser! - table.setValueAt( - new FeatureColour(colorChooser.getColor()), + table.setValueAt(new FeatureColour(colorChooser.getColor()), selectedRow, 1); table.validate(); me.updateFeatureRenderer( @@ -425,8 +440,8 @@ public class FeatureSettings extends JPanel implements false, type); } }); - JMenuItem clearCols = new JMenuItem( - MessageManager.getString("label.select_columns_not_containing")); + JMenuItem clearCols = new JMenuItem(MessageManager + .getString("label.select_columns_not_containing")); clearCols.addActionListener(new ActionListener() { @Override @@ -469,50 +484,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++; } } @@ -531,30 +522,19 @@ 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)); + check.setToolTipText(group); check.addItemListener(new ItemListener() { @Override @@ -578,77 +558,67 @@ public class FeatureSettings extends JPanel implements synchronized void resetTable(String[] groupChanged) { - if (resettingTable == true) + if (resettingTable) { return; } resettingTable = true; - typeWidth = new Hashtable(); + 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()); - } - avWidth[0]++; - if (tmpfeatures[index].getBegin() > tmpfeatures[index].getEnd()) - { - avWidth[1] += 1 + tmpfeatures[index].getBegin() - - tmpfeatures[index].getEnd(); + visibleGroups.add(group); } - 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()) @@ -664,28 +634,29 @@ 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; } data[dataIndex][0] = type; data[dataIndex][1] = fr.getFeatureStyle(type); - data[dataIndex][2] = new Boolean(af.getViewport() - .getFeaturesDisplayed().isVisible(type)); + 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); @@ -698,6 +669,7 @@ public class FeatureSettings extends JPanel implements data[dataIndex][2] = new Boolean(true); dataIndex++; + displayableTypes.remove(type); } if (originalData == null) @@ -708,24 +680,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 @@ -757,8 +810,8 @@ public class FeatureSettings extends JPanel implements JalviewFileChooser chooser = new JalviewFileChooser("fc", "Sequence Feature Colours"); chooser.setFileView(new JalviewFileView()); - chooser.setDialogTitle(MessageManager - .getString("label.load_feature_colours")); + chooser.setDialogTitle( + MessageManager.getString("label.load_feature_colours")); chooser.setToolTipText(MessageManager.getString("action.load")); int value = chooser.showOpenDialog(this); @@ -769,8 +822,8 @@ public class FeatureSettings extends JPanel implements try { - InputStreamReader in = new InputStreamReader(new FileInputStream( - file), "UTF-8"); + InputStreamReader in = new InputStreamReader( + new FileInputStream(file), "UTF-8"); JalviewUserColours jucs = JalviewUserColours.unmarshal(in); @@ -849,8 +902,8 @@ public class FeatureSettings extends JPanel implements JalviewFileChooser chooser = new JalviewFileChooser("fc", "Sequence Feature Colours"); chooser.setFileView(new JalviewFileView()); - chooser.setDialogTitle(MessageManager - .getString("label.save_feature_colours")); + chooser.setDialogTitle( + MessageManager.getString("label.save_feature_colours")); chooser.setToolTipText(MessageManager.getString("action.save")); int value = chooser.showSaveDialog(this); @@ -892,13 +945,13 @@ public class FeatureSettings extends JPanel implements col.setRGB(Format.getHexString(fcol.getMaxColour())); col.setMin(fcol.getMin()); col.setMax(fcol.getMax()); - col.setMinRGB(jalview.util.Format.getHexString(fcol - .getMinColour())); + col.setMinRGB( + jalview.util.Format.getHexString(fcol.getMinColour())); col.setAutoScale(fcol.isAutoScaled()); col.setThreshold(fcol.getThreshold()); col.setColourByLabel(fcol.isColourByLabel()); - col.setThreshType(fcol.isAboveThreshold() ? "ABOVE" : (fcol - .isBelowThreshold() ? "BELOW" : "NONE")); + col.setThreshType(fcol.isAboveThreshold() ? "ABOVE" + : (fcol.isBelowThreshold() ? "BELOW" : "NONE")); } ucs.addColour(col); } @@ -1065,6 +1118,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() @@ -1097,8 +1154,8 @@ public class FeatureSettings extends JPanel implements } }); sortByDens.setFont(JvSwingUtils.getLabelFont()); - sortByDens.setText(MessageManager - .getString("label.sequence_sort_by_density")); + sortByDens.setText( + MessageManager.getString("label.sequence_sort_by_density")); sortByDens.addActionListener(new ActionListener() { @Override @@ -1186,14 +1243,17 @@ public class FeatureSettings extends JPanel implements @Override public void stateChanged(ChangeEvent evt) { - fr.setTransparency((100 - transparency.getValue()) / 100f); - af.alignPanel.paintAlignment(true); + if (!inConstruction) + { + fr.setTransparency((100 - transparency.getValue()) / 100f); + af.alignPanel.paintAlignment(true); + } } }); transparency.setMaximum(70); - transparency.setToolTipText(MessageManager - .getString("label.transparency_tip")); + transparency.setToolTipText( + MessageManager.getString("label.transparency_tip")); fetchDAS.setText(MessageManager.getString("label.fetch_das_features")); fetchDAS.addActionListener(new ActionListener() { @@ -1403,15 +1463,10 @@ public class FeatureSettings extends JPanel implements public void noDasSourceActive() { complete(); - JvOptionPane - .showInternalConfirmDialog( - Desktop.desktop, - MessageManager - .getString("label.no_das_sources_selected_warn"), - MessageManager - .getString("label.no_das_sources_selected_title"), - JvOptionPane.DEFAULT_OPTION, - JvOptionPane.INFORMATION_MESSAGE); + JvOptionPane.showInternalConfirmDialog(Desktop.desktop, + MessageManager.getString("label.no_das_sources_selected_warn"), + MessageManager.getString("label.no_das_sources_selected_title"), + JvOptionPane.DEFAULT_OPTION, JvOptionPane.INFORMATION_MESSAGE); } // /////////////////////////////////////////////////////////////////////// @@ -1508,9 +1563,8 @@ public class FeatureSettings extends JPanel implements } @Override - public Component getTableCellRendererComponent(JTable tbl, - Object color, boolean isSelected, boolean hasFocus, int row, - int column) + public Component getTableCellRendererComponent(JTable tbl, Object color, + boolean isSelected, boolean hasFocus, int row, int column) { FeatureColourI cellColour = (FeatureColourI) color; // JLabel comp = new JLabel(); @@ -1718,8 +1772,8 @@ class FeatureIcon implements Icon } } -class ColorEditor extends AbstractCellEditor implements TableCellEditor, - ActionListener +class ColorEditor extends AbstractCellEditor + implements TableCellEditor, ActionListener { FeatureSettings me;