static String lastFeatureGroupAdded = "Jalview";
/*
- * index into a list of features if more than one selected for editing
+ * the sequence(s) with features to be created / amended
+ */
+ final List<SequenceI> sequences;
+
+ /*
+ * the features (or template features) to be created / amended
+ */
+ final List<SequenceFeature> features;
+
+ /*
+ * true if the dialog is to create a new feature, false if
+ * for amend or delete of existing feature(s)
+ */
+ final boolean forCreate;
+
+ /*
+ * index into the list of features
*/
int featureIndex;
AlignmentPanel ap;
+ Object[] options;
+
+ JTextField name;
+
+ JTextField group;
+
+ JTextArea description;
+
+ JSpinner start;
+
+ JSpinner end;
+
+ JPanel mainPanel;
+
/**
* Constructor
*
* @param alignPanel
+ * @param seqs
+ * @param feats
+ * @param create
+ * if true create a new feature, else amend or delete an existing
+ * feature
*/
- public FeatureEditor(AlignmentPanel alignPanel)
+ public FeatureEditor(AlignmentPanel alignPanel, List<SequenceI> seqs,
+ List<SequenceFeature> feats, boolean create)
{
ap = alignPanel;
fr = alignPanel.getSeqPanel().seqCanvas.fr;
- }
+ sequences = seqs;
+ features = feats;
+ this.forCreate = create;
- public void amendFeatures(final List<SequenceI> sequences,
- final List<SequenceFeature> features,
- final Runnable responseHandler)
- {
- amendFeatures(sequences, features, false, responseHandler);
- }
-
- public void createFeatures(final List<SequenceI> sequences,
- final List<SequenceFeature> features,
- final Runnable responseHandler)
- {
- amendFeatures(sequences, features, true, responseHandler);
+ init();
}
/**
- * Presents a dialog allowing the user to add new features, or amend or delete
- * existing features. Currently this can be on
- * <ul>
- * <li>double-click on a sequence - Amend/Delete features at position</li>
- * <li>Create sequence feature from pop-up menu on selected region</li>
- * <li>Create features for pattern matches from Find</li>
- * </ul>
- * If the supplied feature type is null, show (and update on confirm) the type
- * and group of the last new feature created (with initial defaults of
- * "feature_1" and "Jalview").
- *
- * @param sequences
- * the sequences features are to be created on (if creating
- * features), or a single sequence (if amending features)
- * @param features
- * the current features at the position (if amending), or template
- * new feature(s) with start/end position set (if creating)
- * @param create
- * true to create features, false to amend or delete
- * @param alignPanel
- * @param responseHandler
- * boolean true RunResponse is run if features are created
+ * Initialise the layout and controls
*/
- private void amendFeatures(final List<SequenceI> sequences,
- final List<SequenceFeature> features, boolean create,
- final Runnable responseHandler)
+ protected void init()
{
featureIndex = 0;
- final JPanel mainPanel = new JPanel(new BorderLayout());
+ mainPanel = new JPanel(new BorderLayout());
- final JTextField name = new JTextField(25);
+ name = new JTextField(25);
name.getDocument().addDocumentListener(new DocumentListener()
{
@Override
}
});
- final JTextField group = new JTextField(25);
+ group = new JTextField(25);
group.getDocument().addDocumentListener(new DocumentListener()
{
@Override
}
});
- final JTextArea description = new JTextArea(3, 25);
- final JSpinner start = new JSpinner();
- final JSpinner end = new JSpinner();
+ description = new JTextArea(3, 25);
+ start = new JSpinner();
+ end = new JSpinner();
start.setPreferredSize(new Dimension(80, 20));
end.setPreferredSize(new Dimension(80, 20));
final JLabel colour = new JLabel();
colour.setOpaque(true);
- // colour.setBorder(BorderFactory.createEtchedBorder());
colour.setMaximumSize(new Dimension(30, 16));
colour.addMouseListener(new MouseAdapter()
{
- /*
- * open colour chooser on click in colour panel
- */
@Override
public void mousePressed(MouseEvent evt)
{
if (featureColour.isSimpleColour())
{
+ /*
+ * open colour chooser on click in colour panel
+ */
String title = MessageManager
.getString("label.select_feature_colour");
ColourChooserListener listener = new ColourChooserListener()
});
JPanel gridPanel = new JPanel(new GridLayout(3, 1));
- if (!create && features.size() > 1)
+ if (!forCreate && features.size() > 1)
{
/*
* more than one feature at selected position -
description.setLineWrap(true);
descriptionPanel.add(new JScrollPane(description));
- if (!create)
+ if (!forCreate)
{
mainPanel.add(descriptionPanel, BorderLayout.SOUTH);
featureColour = fr.getFeatureStyle(featureType);
oldColour = featureColour;
updateColourButton(mainPanel, colour, oldColour);
- Object[] options;
- if (!create)
+ if (forCreate)
{
- options = new Object[] { MessageManager.getString("label.amend"),
- MessageManager.getString("action.delete"),
+ options = new Object[] { MessageManager.getString("action.ok"),
MessageManager.getString("action.cancel") };
}
else
{
- options = new Object[] { MessageManager.getString("action.ok"),
+ options = new Object[] { MessageManager.getString("label.amend"),
+ MessageManager.getString("action.delete"),
MessageManager.getString("action.cancel") };
}
+ }
+
+ /**
+ * Presents a dialog allowing the user to add new features, or amend or delete
+ * an existing feature. Currently this can be on
+ * <ul>
+ * <li>double-click on a sequence - Amend/Delete features at position</li>
+ * <li>Create sequence feature from pop-up menu on selected region</li>
+ * <li>Create features for pattern matches from Find</li>
+ * </ul>
+ * If the supplied feature type is null, show (and update on confirm) the type
+ * and group of the last new feature created (with initial defaults of
+ * "feature_1" and "Jalview").
+ */
+ public void showDialog()
+ {
+ RunResponse okAction = forCreate ? getCreateAction() : getAmendAction();
+ RunResponse deleteAction = getDeleteAction();
+ Runnable cancelAction = getCancelAction();
- String title = create
+ String title = forCreate
? MessageManager.getString("label.create_new_sequence_features")
: MessageManager.formatMessage("label.amend_delete_features",
new String[]
{ sequences.get(0).getName() });
- /*
- * register responses and show the dialog
- */
- JvOptionPane.newOptionDialog(Desktop.desktop).response(
-
- new RunResponse(JvOptionPane.OK_OPTION)
- {
- public void run()
- {
- final String enteredType = name.getText().trim();
- final String enteredGroup = group.getText().trim();
- final String enteredDescription = description.getText()
- .replaceAll("\n", " ");
- if (enteredType.length() > 0)
-
- {
- /*
- * update default values only if creating using default values
- */
- if (useLastDefaults)
- {
- lastFeatureAdded = enteredType;
- lastFeatureGroupAdded = enteredGroup;
- // TODO: determine if the null feature group is valid
- if (lastFeatureGroupAdded.length() < 1)
- {
- lastFeatureGroupAdded = null;
- }
- }
- }
-
- if (create)
- {
- // NEW FEATURES ADDED
- if (enteredType.length() > 0)
- {
- for (int i = 0; i < sequences.size(); i++)
- {
- SequenceFeature sf = features.get(i);
- SequenceFeature sf2 = new SequenceFeature(enteredType,
- enteredDescription, sf.getBegin(),
- sf.getEnd(), enteredGroup);
- new FeaturesFile().parseDescriptionHTML(sf2, false);
- sequences.get(i).addSequenceFeature(sf2);
- }
-
- fr.setColour(enteredType, featureColour);
-
- fr.featuresAdded();
-
- responseHandler.run();
- }
- }
- else
- {
- SequenceFeature sf = features.get(featureIndex);
- /*
- * Feature amended - YES_OPTION corresponds to the Amend button
- * need to refresh Feature Settings if type, group or colour changed;
- * note we don't force the feature to be visible - the user has been
- * warned if a hidden feature type or group was entered
- */
- boolean refreshSettings = (!featureType
- .equals(enteredType)
- || !featureGroup.equals(enteredGroup));
- refreshSettings |= (featureColour != oldColour);
- fr.setColour(enteredType, featureColour);
- int newBegin = sf.begin;
- int newEnd = sf.end;
- try
- {
- 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(sf,
- enteredType, newBegin, newEnd, enteredGroup,
- sf.getScore());
- newSf.setDescription(enteredDescription);
- new FeaturesFile().parseDescriptionHTML(newSf, false);
- // amend features dialog only updates one sequence at a time
- sequences.get(0).addSequenceFeature(newSf);
-
- if (refreshSettings)
- {
- fr.featuresAdded();
- }
- }
-
- /*
- * suppress fastPaint here - if feature colour changed,
- * we need to repaint the whole alignment
- */
- ap.getSeqPanel().seqCanvas.highlightSearchResults(null);
- ap.paintAlignment(true, true);
- }
- }).response(new RunResponse(JvOptionPane.NO_OPTION)
- {
- public void run()
- {
- SequenceFeature sf = features.get(featureIndex);
- /*
- * NO_OPTION corresponds to the Delete button
- */
- sequences.get(0).getDatasetSequence().deleteFeature(sf);
- // update Feature Settings for removal of feature / group
- fr.featuresAdded();
- ap.getSeqPanel().seqCanvas.highlightSearchResults(null);
- ap.paintAlignment(true, true);
- }
- }).defaultResponse(new Runnable()
- {
- public void run()
- {
- ap.getSeqPanel().seqCanvas.highlightSearchResults(null);
- ap.paintAlignment(true, true);
- }
- }).showInternalDialog(mainPanel, title,
+ JvOptionPane.newOptionDialog(Desktop.desktop).response(okAction)
+ .response(deleteAction).defaultResponse(cancelAction)
+ .showInternalDialog(mainPanel, title,
JvOptionPane.YES_NO_CANCEL_OPTION,
JvOptionPane.QUESTION_MESSAGE, null, options,
MessageManager.getString("action.ok"));
}
/**
+ * Answers an action to run on Cancel in the dialog. This is just to remove
+ * any feature highlighting from the display. Changes in the dialog are not
+ * applied until it is dismissed with OK, Amend or Delete, so there are no
+ * updates to reset on Cancel.
+ *
+ * @return
+ */
+ protected Runnable getCancelAction()
+ {
+ Runnable defaultResponse = new Runnable()
+ {
+ public void run()
+ {
+ ap.highlightSearchResults(null);
+ ap.paintAlignment(false, false);
+ }
+ };
+ return defaultResponse;
+ }
+
+ /**
+ * Returns the action to be run on OK in the dialog when creating one or more
+ * sequence features. Note these may have a pre-supplied feature type (such as
+ * a Find pattern), or none, in which case the feature type and group default
+ * to those last added through this dialog. The action includes refreshing the
+ * Feature Settings panel (if it is open), to show any new feature type, or
+ * amended colour for an existing type.
+ *
+ * @return
+ */
+ protected RunResponse getCreateAction()
+ {
+ RunResponse okAction = new RunResponse(JvOptionPane.OK_OPTION)
+ {
+ boolean useLastDefaults = features.get(0).getType() == null;
+
+ public void run()
+ {
+ final String enteredType = name.getText().trim();
+ final String enteredGroup = group.getText().trim();
+ final String enteredDescription = description.getText()
+ .replaceAll("\n", " ");
+ if (enteredType.length() > 0)
+ {
+ /*
+ * update default values only if creating using default values
+ */
+ if (useLastDefaults)
+ {
+ lastFeatureAdded = enteredType;
+ lastFeatureGroupAdded = enteredGroup;
+ // TODO: determine if the null feature group is valid
+ if (lastFeatureGroupAdded.length() < 1)
+ {
+ lastFeatureGroupAdded = null;
+ }
+ }
+ }
+
+ if (enteredType.length() > 0)
+ {
+ for (int i = 0; i < sequences.size(); i++)
+ {
+ SequenceFeature sf = features.get(i);
+ SequenceFeature sf2 = new SequenceFeature(enteredType,
+ enteredDescription, sf.getBegin(), sf.getEnd(),
+ enteredGroup);
+ new FeaturesFile().parseDescriptionHTML(sf2, false);
+ sequences.get(i).addSequenceFeature(sf2);
+ }
+
+ fr.setColour(enteredType, featureColour);
+ fr.featuresAdded();
+
+ repaintPanel();
+ }
+ }
+ };
+ return okAction;
+ }
+
+ /**
+ * Answers the action to run on Delete in the dialog. Note this includes
+ * refreshing the Feature Settings (if open) in case the only instance of a
+ * feature type or group has been deleted.
+ *
+ * @return
+ */
+ protected RunResponse getDeleteAction()
+ {
+ RunResponse deleteAction = new RunResponse(JvOptionPane.NO_OPTION)
+ {
+ public void run()
+ {
+ SequenceFeature sf = features.get(featureIndex);
+ sequences.get(0).getDatasetSequence().deleteFeature(sf);
+ fr.featuresAdded();
+ ap.getSeqPanel().seqCanvas.highlightSearchResults(null);
+ ap.paintAlignment(true, true);
+ }
+ };
+ return deleteAction;
+ }
+
+ /**
* update the amend feature button dependent on the given style
*
* @param bigPanel
{
colour.removeAll();
colour.setIcon(null);
- colour.setToolTipText(null);
colour.setText("");
if (col.isSimpleColour())
{
+ colour.setToolTipText(null);
colour.setBackground(col.getColour());
}
else
{
colour.setBackground(bigPanel.getBackground());
colour.setForeground(Color.black);
+ colour.setToolTipText(FeatureSettings.getColorTooltip(col, false));
FeatureSettings.renderGraduatedColor(colour, col);
}
}
}
}
+ /**
+ * On closing the dialog - ensure feature display is turned on, to show any
+ * new features - remove highlighting of the last selected feature - repaint
+ * the panel to show any changes
+ */
+ protected void repaintPanel()
+ {
+ ap.alignFrame.showSeqFeatures.setSelected(true);
+ ap.av.setShowSequenceFeatures(true);
+ ap.av.setSearchResults(null);
+ ap.paintAlignment(true, true);
+ }
+
+ /**
+ * Returns the action to be run on OK in the dialog when amending a feature.
+ * Note this may include refreshing the Feature Settings panel (if it is
+ * open), if feature type, group or colour has changed (but not for
+ * description or extent).
+ *
+ * @return
+ */
+ protected RunResponse getAmendAction()
+ {
+ RunResponse okAction = new RunResponse(JvOptionPane.OK_OPTION)
+ {
+ boolean useLastDefaults = features.get(0).getType() == null;
+
+ String featureType = name.getText();
+
+ String featureGroup = group.getText();
+
+ public void run()
+ {
+ final String enteredType = name.getText().trim();
+ final String enteredGroup = group.getText().trim();
+ final String enteredDescription = description.getText()
+ .replaceAll("\n", " ");
+ if (enteredType.length() > 0)
+
+ {
+ /*
+ * update default values only if creating using default values
+ */
+ if (useLastDefaults)
+ {
+ lastFeatureAdded = enteredType;
+ lastFeatureGroupAdded = enteredGroup;
+ // TODO: determine if the null feature group is valid
+ if (lastFeatureGroupAdded.length() < 1)
+ {
+ lastFeatureGroupAdded = null;
+ }
+ }
+ }
+
+ SequenceFeature sf = features.get(featureIndex);
+
+ /*
+ * Need to refresh Feature Settings if type, group or colour changed;
+ * note we don't force the feature to be visible - the user has been
+ * warned if a hidden feature type or group was entered
+ */
+ boolean refreshSettings = (!featureType.equals(enteredType)
+ || !featureGroup.equals(enteredGroup));
+ refreshSettings |= (featureColour != oldColour);
+ fr.setColour(enteredType, featureColour);
+ int newBegin = sf.begin;
+ int newEnd = sf.end;
+ try
+ {
+ newBegin = ((Integer) start.getValue()).intValue();
+ newEnd = ((Integer) end.getValue()).intValue();
+ } catch (NumberFormatException ex)
+ {
+ // JSpinner doesn't accept invalid format data :-)
+ }
+
+ /*
+ * 'amend' the feature by deleting it and adding a new one
+ * (to ensure integrity of SequenceFeatures data store)
+ * note this dialog only updates one sequence at a time
+ */
+ sequences.get(0).deleteFeature(sf);
+ SequenceFeature newSf = new SequenceFeature(sf, enteredType,
+ newBegin, newEnd, enteredGroup, sf.getScore());
+ newSf.setDescription(enteredDescription);
+ new FeaturesFile().parseDescriptionHTML(newSf, false);
+ sequences.get(0).addSequenceFeature(newSf);
+
+ if (refreshSettings)
+ {
+ fr.featuresAdded();
+ }
+ repaintPanel();
+ }
+ };
+ return okAction;
+ }
+
}