Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.gui

File FeatureEditor.java

 

Coverage histogram

../../img/srcFileCovDistChart0.png
56% of files have more coverage

Code metrics

50
214
25
1
686
474
52
0.24
8.56
25
2.08

Classes

Class Line # Actions
FeatureEditor 44 214 52 289
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    package jalview.gui;
2   
3    import jalview.api.FeatureColourI;
4    import jalview.datamodel.SearchResults;
5    import jalview.datamodel.SearchResultsI;
6    import jalview.datamodel.SequenceFeature;
7    import jalview.datamodel.SequenceI;
8    import jalview.gui.JalviewColourChooser.ColourChooserListener;
9    import jalview.io.FeaturesFile;
10    import jalview.schemes.FeatureColour;
11    import jalview.util.ColorUtils;
12    import jalview.util.MessageManager;
13    import jalview.util.dialogrunner.RunResponse;
14   
15    import java.awt.BorderLayout;
16    import java.awt.Color;
17    import java.awt.Dimension;
18    import java.awt.Font;
19    import java.awt.GridLayout;
20    import java.awt.event.ActionEvent;
21    import java.awt.event.ActionListener;
22    import java.awt.event.ItemEvent;
23    import java.awt.event.ItemListener;
24    import java.awt.event.MouseAdapter;
25    import java.awt.event.MouseEvent;
26    import java.util.ArrayList;
27    import java.util.List;
28   
29    import javax.swing.JComboBox;
30    import javax.swing.JLabel;
31    import javax.swing.JPanel;
32    import javax.swing.JScrollPane;
33    import javax.swing.JSpinner;
34    import javax.swing.JTextArea;
35    import javax.swing.JTextField;
36    import javax.swing.SwingConstants;
37    import javax.swing.event.DocumentEvent;
38    import javax.swing.event.DocumentListener;
39   
40    /**
41    * Provides a dialog allowing the user to add new features, or amend or delete
42    * existing features
43    */
 
44    public class FeatureEditor
45    {
46    /*
47    * defaults for creating a new feature are the last created
48    * feature type and group
49    */
50    static String lastFeatureAdded = "feature_1";
51   
52    static String lastFeatureGroupAdded = "Jalview";
53   
54    /*
55    * the sequence(s) with features to be created / amended
56    */
57    final List<SequenceI> sequences;
58   
59    /*
60    * the features (or template features) to be created / amended
61    */
62    final List<SequenceFeature> features;
63   
64    /*
65    * true if the dialog is to create a new feature, false if
66    * for amend or delete of existing feature(s)
67    */
68    final boolean forCreate;
69   
70    /*
71    * index into the list of features
72    */
73    int featureIndex;
74   
75    FeatureColourI oldColour;
76   
77    FeatureColourI featureColour;
78   
79    FeatureRenderer fr;
80   
81    AlignmentPanel ap;
82   
83    JTextField name;
84   
85    JTextField group;
86   
87    JTextArea description;
88   
89    JSpinner start;
90   
91    JSpinner end;
92   
93    JPanel mainPanel;
94   
95    /**
96    * Constructor
97    *
98    * @param alignPanel
99    * @param seqs
100    * @param feats
101    * @param create
102    * if true create a new feature, else amend or delete an existing
103    * feature
104    */
 
105  0 toggle public FeatureEditor(AlignmentPanel alignPanel, List<SequenceI> seqs,
106    List<SequenceFeature> feats, boolean create)
107    {
108  0 ap = alignPanel;
109  0 fr = alignPanel.getSeqPanel().seqCanvas.fr;
110  0 sequences = seqs;
111  0 features = feats;
112  0 this.forCreate = create;
113   
114  0 init();
115    }
116   
117    /**
118    * Initialise the layout and controls
119    */
 
120  0 toggle protected void init()
121    {
122  0 featureIndex = 0;
123   
124  0 mainPanel = new JPanel(new BorderLayout());
125   
126  0 name = new JTextField(25);
127  0 name.getDocument().addDocumentListener(new DocumentListener()
128    {
 
129  0 toggle @Override
130    public void insertUpdate(DocumentEvent e)
131    {
132  0 warnIfTypeHidden(mainPanel, name.getText());
133    }
134   
 
135  0 toggle @Override
136    public void removeUpdate(DocumentEvent e)
137    {
138  0 warnIfTypeHidden(mainPanel, name.getText());
139    }
140   
 
141  0 toggle @Override
142    public void changedUpdate(DocumentEvent e)
143    {
144  0 warnIfTypeHidden(mainPanel, name.getText());
145    }
146    });
147   
148  0 group = new JTextField(25);
149  0 group.getDocument().addDocumentListener(new DocumentListener()
150    {
 
151  0 toggle @Override
152    public void insertUpdate(DocumentEvent e)
153    {
154  0 warnIfGroupHidden(mainPanel, group.getText());
155    }
156   
 
157  0 toggle @Override
158    public void removeUpdate(DocumentEvent e)
159    {
160  0 warnIfGroupHidden(mainPanel, group.getText());
161    }
162   
 
163  0 toggle @Override
164    public void changedUpdate(DocumentEvent e)
165    {
166  0 warnIfGroupHidden(mainPanel, group.getText());
167    }
168    });
169   
170  0 description = new JTextArea(3, 25);
171  0 start = new JSpinner();
172  0 end = new JSpinner();
173  0 start.setPreferredSize(new Dimension(80, 20));
174  0 end.setPreferredSize(new Dimension(80, 20));
175  0 final JLabel colour = new JLabel();
176  0 colour.setOpaque(true);
177  0 colour.setMaximumSize(new Dimension(30, 16));
178  0 colour.addMouseListener(new MouseAdapter()
179    {
 
180  0 toggle @Override
181    public void mousePressed(MouseEvent evt)
182    {
183  0 if (featureColour.isSimpleColour())
184    {
185    /*
186    * open colour chooser on click in colour panel
187    */
188  0 String title = MessageManager
189    .getString("label.select_feature_colour");
190  0 ColourChooserListener listener = new ColourChooserListener()
191    {
 
192  0 toggle @Override
193    public void colourSelected(Color c)
194    {
195  0 featureColour = new FeatureColour(c);
196  0 updateColourButton(mainPanel, colour, featureColour);
197    };
198    };
199  0 JalviewColourChooser.showColourChooser(Desktop.getDesktop(),
200    title, featureColour.getColour(), listener);
201    }
202    else
203    {
204    /*
205    * variable colour dialog - on OK, refetch the updated
206    * feature colour and update this display
207    */
208  0 final String ft = features.get(featureIndex).getType();
209  0 final String type = ft == null ? lastFeatureAdded : ft;
210  0 FeatureTypeSettings fcc = new FeatureTypeSettings(fr, type);
211  0 fcc.setRequestFocusEnabled(true);
212  0 fcc.requestFocus();
213  0 fcc.addActionListener(new ActionListener()
214    {
 
215  0 toggle @Override
216    public void actionPerformed(ActionEvent e)
217    {
218  0 featureColour = fr.getFeatureStyle(ft);
219  0 fr.setColour(type, featureColour);
220  0 updateColourButton(mainPanel, colour, featureColour);
221    }
222    });
223    }
224    }
225    });
226  0 JPanel gridPanel = new JPanel(new GridLayout(3, 1));
227   
228  0 if (!forCreate && features.size() > 1)
229    {
230    /*
231    * more than one feature at selected position -
232    * add a drop-down to choose the feature to amend
233    * space pad text if necessary to make entries distinct
234    */
235  0 gridPanel = new JPanel(new GridLayout(4, 1));
236  0 JPanel choosePanel = new JPanel();
237  0 choosePanel.add(new JLabel(
238    MessageManager.getString("label.select_feature") + ":"));
239  0 final JComboBox<String> overlaps = new JComboBox<>();
240  0 List<String> added = new ArrayList<>();
241  0 for (SequenceFeature sf : features)
242    {
243  0 String text = String.format("%s/%d-%d (%s)", sf.getType(),
244    sf.getBegin(), sf.getEnd(), sf.getFeatureGroup());
245  0 while (added.contains(text))
246    {
247  0 text += " ";
248    }
249  0 overlaps.addItem(text);
250  0 added.add(text);
251    }
252  0 choosePanel.add(overlaps);
253   
254  0 overlaps.addItemListener(new ItemListener()
255    {
 
256  0 toggle @Override
257    public void itemStateChanged(ItemEvent e)
258    {
259  0 int index = overlaps.getSelectedIndex();
260  0 if (index != -1)
261    {
262  0 featureIndex = index;
263  0 SequenceFeature sf = features.get(index);
264  0 name.setText(sf.getType());
265  0 description.setText(sf.getDescription());
266  0 group.setText(sf.getFeatureGroup());
267  0 start.setValue(new Integer(sf.getBegin()));
268  0 end.setValue(new Integer(sf.getEnd()));
269   
270  0 SearchResultsI highlight = new SearchResults();
271  0 highlight.addResult(sequences.get(0), sf.getBegin(),
272    sf.getEnd());
273   
274  0 ap.getSeqPanel().seqCanvas.highlightSearchResults(highlight);
275    }
276  0 FeatureColourI col = fr.getFeatureStyle(name.getText());
277  0 if (col == null)
278    {
279  0 col = new FeatureColour(
280    ColorUtils.createColourFromName(name.getText()));
281    }
282  0 oldColour = featureColour = col;
283  0 updateColourButton(mainPanel, colour, col);
284    }
285    });
286   
287  0 gridPanel.add(choosePanel);
288    }
289   
290  0 JPanel namePanel = new JPanel();
291  0 gridPanel.add(namePanel);
292  0 namePanel.add(new JLabel(MessageManager.getString("label.name:"),
293    JLabel.RIGHT));
294  0 namePanel.add(name);
295   
296  0 JPanel groupPanel = new JPanel();
297  0 gridPanel.add(groupPanel);
298  0 groupPanel.add(new JLabel(MessageManager.getString("label.group:"),
299    JLabel.RIGHT));
300  0 groupPanel.add(group);
301   
302  0 JPanel colourPanel = new JPanel();
303  0 gridPanel.add(colourPanel);
304  0 colourPanel.add(new JLabel(MessageManager.getString("label.colour"),
305    JLabel.RIGHT));
306  0 colourPanel.add(colour);
307  0 colour.setPreferredSize(new Dimension(150, 15));
308  0 colour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 9));
309  0 colour.setForeground(Color.black);
310  0 colour.setHorizontalAlignment(SwingConstants.CENTER);
311  0 colour.setVerticalAlignment(SwingConstants.CENTER);
312  0 colour.setHorizontalTextPosition(SwingConstants.CENTER);
313  0 colour.setVerticalTextPosition(SwingConstants.CENTER);
314  0 mainPanel.add(gridPanel, BorderLayout.NORTH);
315   
316  0 JPanel descriptionPanel = new JPanel();
317  0 descriptionPanel.add(new JLabel(
318    MessageManager.getString("label.description:"), JLabel.RIGHT));
319  0 description.setFont(JvSwingUtils.getTextAreaFont());
320  0 description.setLineWrap(true);
321  0 descriptionPanel.add(new JScrollPane(description));
322   
323  0 if (!forCreate)
324    {
325  0 mainPanel.add(descriptionPanel, BorderLayout.SOUTH);
326   
327  0 JPanel startEndPanel = new JPanel();
328  0 startEndPanel.add(new JLabel(MessageManager.getString("label.start"),
329    JLabel.RIGHT));
330  0 startEndPanel.add(start);
331  0 startEndPanel.add(new JLabel(MessageManager.getString("label.end"),
332    JLabel.RIGHT));
333  0 startEndPanel.add(end);
334  0 mainPanel.add(startEndPanel, BorderLayout.CENTER);
335    }
336    else
337    {
338  0 mainPanel.add(descriptionPanel, BorderLayout.CENTER);
339    }
340   
341    /*
342    * default feature type and group to that of the first feature supplied,
343    * or to the last feature created if not supplied (null value)
344    */
345  0 SequenceFeature firstFeature = features.get(0);
346  0 boolean useLastDefaults = firstFeature.getType() == null;
347  0 final String featureType = useLastDefaults ? lastFeatureAdded
348    : firstFeature.getType();
349  0 final String featureGroup = useLastDefaults ? lastFeatureGroupAdded
350    : firstFeature.getFeatureGroup();
351  0 name.setText(featureType);
352  0 group.setText(featureGroup);
353   
354  0 start.setValue(new Integer(firstFeature.getBegin()));
355  0 end.setValue(new Integer(firstFeature.getEnd()));
356  0 description.setText(firstFeature.getDescription());
357  0 featureColour = fr.getFeatureStyle(featureType);
358  0 oldColour = featureColour;
359  0 updateColourButton(mainPanel, colour, oldColour);
360    }
361   
362    /**
363    * Presents a dialog allowing the user to add new features, or amend or delete
364    * an existing feature. Currently this can be on
365    * <ul>
366    * <li>double-click on a sequence - Amend/Delete a selected feature at the
367    * position</li>
368    * <li>Create sequence feature(s) from pop-up menu on selected region</li>
369    * <li>Create features for pattern matches from Find</li>
370    * </ul>
371    * If the supplied feature type is null, show (and update on confirm) the type
372    * and group of the last new feature created (with initial defaults of
373    * "feature_1" and "Jalview").
374    */
 
375  0 toggle public void showDialog()
376    {
377  0 RunResponse okAction = forCreate ? getCreateAction() : getAmendAction();
378  0 RunResponse cancelAction = getCancelAction();
379   
380    /*
381    * set dialog action handlers for OK (create/Amend) and Cancel options
382    * also for Delete if applicable (when amending features)
383    */
384  0 JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop)
385    .response(okAction).response(cancelAction);
386  0 if (!forCreate)
387    {
388  0 dialog.response(getDeleteAction());
389    }
390   
391  0 String title = null;
392  0 Object[] options = null;
393  0 if (forCreate)
394    {
395  0 title = MessageManager
396    .getString("label.create_new_sequence_features");
397  0 options = new Object[] { MessageManager.getString("action.ok"),
398    MessageManager.getString("action.cancel") };
399    }
400    else
401    {
402  0 title = MessageManager.formatMessage("label.amend_delete_features",
403    new String[]
404    { sequences.get(0).getName() });
405  0 options = new Object[] { MessageManager.getString("label.amend"),
406    MessageManager.getString("action.delete"),
407    MessageManager.getString("action.cancel") };
408    }
409   
410  0 dialog.showInternalDialog(mainPanel, title,
411    JvOptionPane.YES_NO_CANCEL_OPTION,
412    JvOptionPane.PLAIN_MESSAGE, null, options,
413    MessageManager.getString("action.ok"));
414    }
415   
416    /**
417    * Answers an action to run on Cancel in the dialog. This is just to remove
418    * any feature highlighting from the display. Changes in the dialog are not
419    * applied until it is dismissed with OK, Amend or Delete, so there are no
420    * updates to reset on Cancel.
421    *
422    * @return
423    */
 
424  0 toggle protected RunResponse getCancelAction()
425    {
426  0 RunResponse okAction = new RunResponse(JvOptionPane.CANCEL_OPTION)
427    {
 
428  0 toggle @Override
429    public void run()
430    {
431  0 ap.highlightSearchResults(null);
432  0 ap.paintAlignment(false, false);
433    }
434    };
435  0 return okAction;
436    }
437   
438    /**
439    * Returns the action to be run on OK in the dialog when creating one or more
440    * sequence features. Note these may have a pre-supplied feature type (such as
441    * a Find pattern), or none, in which case the feature type and group default
442    * to those last added through this dialog. The action includes refreshing the
443    * Feature Settings panel (if it is open), to show any new feature type, or
444    * amended colour for an existing type.
445    *
446    * @return
447    */
 
448  0 toggle protected RunResponse getCreateAction()
449    {
450  0 RunResponse okAction = new RunResponse(JvOptionPane.OK_OPTION)
451    {
452    boolean useLastDefaults = features.get(0).getType() == null;
453   
 
454  0 toggle public void run()
455    {
456  0 final String enteredType = name.getText().trim();
457  0 final String enteredGroup = group.getText().trim();
458  0 final String enteredDescription = description.getText()
459    .replaceAll("\n", " ");
460  0 if (enteredType.length() > 0)
461    {
462    /*
463    * update default values only if creating using default values
464    */
465  0 if (useLastDefaults)
466    {
467  0 lastFeatureAdded = enteredType;
468  0 lastFeatureGroupAdded = enteredGroup;
469    // TODO: determine if the null feature group is valid
470  0 if (lastFeatureGroupAdded.length() < 1)
471    {
472  0 lastFeatureGroupAdded = null;
473    }
474    }
475    }
476   
477  0 if (enteredType.length() > 0)
478    {
479  0 for (int i = 0; i < sequences.size(); i++)
480    {
481  0 SequenceFeature sf = features.get(i);
482  0 SequenceFeature sf2 = new SequenceFeature(enteredType,
483    enteredDescription, sf.getBegin(), sf.getEnd(),
484    enteredGroup);
485  0 new FeaturesFile().parseDescriptionHTML(sf2, false);
486  0 sequences.get(i).addSequenceFeature(sf2);
487    }
488   
489  0 fr.setColour(enteredType, featureColour);
490  0 fr.featuresAdded();
491   
492  0 repaintPanel();
493    }
494    }
495    };
496  0 return okAction;
497    }
498   
499    /**
500    * Answers the action to run on Delete in the dialog. Note this includes
501    * refreshing the Feature Settings (if open) in case the only instance of a
502    * feature type or group has been deleted.
503    *
504    * @return
505    */
 
506  0 toggle protected RunResponse getDeleteAction()
507    {
508  0 RunResponse deleteAction = new RunResponse(JvOptionPane.NO_OPTION)
509    {
 
510  0 toggle public void run()
511    {
512  0 SequenceFeature sf = features.get(featureIndex);
513  0 sequences.get(0).getDatasetSequence().deleteFeature(sf);
514  0 fr.featuresAdded();
515  0 ap.getSeqPanel().seqCanvas.highlightSearchResults(null);
516  0 ap.paintAlignment(true, true);
517    }
518    };
519  0 return deleteAction;
520    }
521   
522    /**
523    * update the amend feature button dependent on the given style
524    *
525    * @param bigPanel
526    * @param col
527    * @param col
528    */
 
529  0 toggle protected void updateColourButton(JPanel bigPanel, JLabel colour,
530    FeatureColourI col)
531    {
532  0 colour.removeAll();
533  0 colour.setIcon(null);
534  0 colour.setText("");
535   
536  0 if (col.isSimpleColour())
537    {
538  0 colour.setToolTipText(null);
539  0 colour.setBackground(col.getColour());
540    }
541    else
542    {
543  0 colour.setBackground(bigPanel.getBackground());
544  0 colour.setForeground(Color.black);
545  0 colour.setToolTipText(FeatureSettings.getColorTooltip(col, false));
546  0 FeatureSettings.renderGraduatedColor(colour, col);
547    }
548    }
549   
550    /**
551    * Show a warning message if the entered group is one that is currently hidden
552    *
553    * @param panel
554    * @param group
555    */
 
556  0 toggle protected void warnIfGroupHidden(JPanel panel, String group)
557    {
558  0 if (!fr.isGroupVisible(group))
559    {
560  0 String msg = MessageManager.formatMessage("label.warning_hidden",
561    MessageManager.getString("label.group"), group);
562  0 JvOptionPane.showMessageDialog(panel, msg, "",
563    JvOptionPane.OK_OPTION);
564    }
565    }
566   
567    /**
568    * Show a warning message if the entered type is one that is currently hidden
569    *
570    * @param panel
571    * @param type
572    */
 
573  0 toggle protected void warnIfTypeHidden(JPanel panel, String type)
574    {
575  0 if (fr.getRenderOrder().contains(type))
576    {
577  0 if (!fr.showFeatureOfType(type))
578    {
579  0 String msg = MessageManager.formatMessage("label.warning_hidden",
580    MessageManager.getString("label.feature_type"), type);
581  0 JvOptionPane.showMessageDialog(panel, msg, "",
582    JvOptionPane.OK_OPTION);
583    }
584    }
585    }
586   
587    /**
588    * On closing the dialog - ensure feature display is turned on, to show any
589    * new features - remove highlighting of the last selected feature - repaint
590    * the panel to show any changes
591    */
 
592  0 toggle protected void repaintPanel()
593    {
594  0 ap.alignFrame.showSeqFeatures.setSelected(true);
595  0 ap.av.setShowSequenceFeatures(true);
596  0 ap.av.setSearchResults(null);
597  0 ap.paintAlignment(true, true);
598    }
599   
600    /**
601    * Returns the action to be run on OK in the dialog when amending a feature.
602    * Note this may include refreshing the Feature Settings panel (if it is
603    * open), if feature type, group or colour has changed (but not for
604    * description or extent).
605    *
606    * @return
607    */
 
608  0 toggle protected RunResponse getAmendAction()
609    {
610  0 RunResponse okAction = new RunResponse(JvOptionPane.OK_OPTION)
611    {
612    boolean useLastDefaults = features.get(0).getType() == null;
613   
614    String featureType = name.getText();
615   
616    String featureGroup = group.getText();
617   
 
618  0 toggle public void run()
619    {
620  0 final String enteredType = name.getText().trim();
621  0 final String enteredGroup = group.getText().trim();
622  0 final String enteredDescription = description.getText()
623    .replaceAll("\n", " ");
624  0 if (enteredType.length() > 0)
625   
626    {
627    /*
628    * update default values only if creating using default values
629    */
630  0 if (useLastDefaults)
631    {
632  0 lastFeatureAdded = enteredType;
633  0 lastFeatureGroupAdded = enteredGroup;
634    // TODO: determine if the null feature group is valid
635  0 if (lastFeatureGroupAdded.length() < 1)
636    {
637  0 lastFeatureGroupAdded = null;
638    }
639    }
640    }
641   
642  0 SequenceFeature sf = features.get(featureIndex);
643   
644    /*
645    * Need to refresh Feature Settings if type, group or colour changed;
646    * note we don't force the feature to be visible - the user has been
647    * warned if a hidden feature type or group was entered
648    */
649  0 boolean refreshSettings = (!featureType.equals(enteredType)
650    || !featureGroup.equals(enteredGroup));
651  0 refreshSettings |= (featureColour != oldColour);
652  0 fr.setColour(enteredType, featureColour);
653  0 int newBegin = sf.begin;
654  0 int newEnd = sf.end;
655  0 try
656    {
657  0 newBegin = ((Integer) start.getValue()).intValue();
658  0 newEnd = ((Integer) end.getValue()).intValue();
659    } catch (NumberFormatException ex)
660    {
661    // JSpinner doesn't accept invalid format data :-)
662    }
663   
664    /*
665    * 'amend' the feature by deleting it and adding a new one
666    * (to ensure integrity of SequenceFeatures data store)
667    * note this dialog only updates one sequence at a time
668    */
669  0 sequences.get(0).deleteFeature(sf);
670  0 SequenceFeature newSf = new SequenceFeature(sf, enteredType,
671    newBegin, newEnd, enteredGroup, sf.getScore());
672  0 newSf.setDescription(enteredDescription);
673  0 new FeaturesFile().parseDescriptionHTML(newSf, false);
674  0 sequences.get(0).addSequenceFeature(newSf);
675   
676  0 if (refreshSettings)
677    {
678  0 fr.featuresAdded();
679    }
680  0 repaintPanel();
681    }
682    };
683  0 return okAction;
684    }
685   
686    }