JAL-964 JAL-2510 update Feature Settings from Amend Features, warn if
[jalview.git] / src / jalview / appletgui / FeatureRenderer.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.appletgui;
22
23 import jalview.api.FeatureColourI;
24 import jalview.datamodel.SearchResults;
25 import jalview.datamodel.SearchResultsI;
26 import jalview.datamodel.SequenceFeature;
27 import jalview.datamodel.SequenceI;
28 import jalview.io.FeaturesFile;
29 import jalview.schemes.FeatureColour;
30 import jalview.util.ColorUtils;
31 import jalview.util.MessageManager;
32 import jalview.viewmodel.AlignmentViewport;
33
34 import java.awt.BorderLayout;
35 import java.awt.Button;
36 import java.awt.Choice;
37 import java.awt.Color;
38 import java.awt.Dimension;
39 import java.awt.FlowLayout;
40 import java.awt.Font;
41 import java.awt.Frame;
42 import java.awt.Graphics;
43 import java.awt.GridLayout;
44 import java.awt.Label;
45 import java.awt.Panel;
46 import java.awt.ScrollPane;
47 import java.awt.TextArea;
48 import java.awt.TextField;
49 import java.awt.event.ActionEvent;
50 import java.awt.event.ActionListener;
51 import java.awt.event.MouseAdapter;
52 import java.awt.event.MouseEvent;
53 import java.awt.event.TextEvent;
54 import java.awt.event.TextListener;
55 import java.util.Hashtable;
56
57 /**
58  * DOCUMENT ME!
59  * 
60  * @author $author$
61  * @version $Revision$
62  */
63 public class FeatureRenderer extends
64         jalview.renderer.seqfeatures.FeatureRenderer
65 {
66
67   // Holds web links for feature groups and feature types
68   // in the form label|link
69   Hashtable featureLinks = null;
70
71   /**
72    * Creates a new FeatureRenderer object.
73    * 
74    * @param av
75    */
76   public FeatureRenderer(AlignmentViewport av)
77   {
78     super(av);
79
80   }
81
82   static String lastFeatureAdded;
83
84   static String lastFeatureGroupAdded;
85
86   static String lastDescriptionAdded;
87
88   int featureIndex = 0;
89
90   boolean deleteFeature = false;
91
92   FeatureColourPanel colourPanel;
93
94   class FeatureColourPanel extends Panel
95   {
96     String label = "";
97
98     private Color maxCol;
99
100     private boolean isColourByLabel, isGcol;
101
102     /**
103      * render a feature style in the amend feature dialog box
104      */
105     public void updateColor(FeatureColourI newcol)
106     {
107       Color bg = null;
108       String vlabel = "";
109       if (newcol.isSimpleColour())
110       {
111         bg = newcol.getColour();
112         setBackground(bg);
113       }
114       else
115       {
116         if (newcol.isAboveThreshold())
117         {
118           vlabel += " (>)";
119         }
120         else if (newcol.isBelowThreshold())
121         {
122           vlabel += " (<)";
123         }
124
125         if (isColourByLabel = newcol.isColourByLabel())
126         {
127           setBackground(bg = Color.white);
128           vlabel += " (by Label)";
129         }
130         else
131         {
132           setBackground(bg = newcol.getMinColour());
133           maxCol = newcol.getMaxColour();
134         }
135       }
136       label = vlabel;
137       setBackground(bg);
138       repaint();
139     }
140
141     FeatureColourPanel()
142     {
143       super(null);
144     }
145
146     @Override
147     public void paint(Graphics g)
148     {
149       Dimension d = getSize();
150       if (isGcol)
151       {
152         if (isColourByLabel)
153         {
154           g.setColor(Color.white);
155           g.fillRect(d.width / 2, 0, d.width / 2, d.height);
156           g.setColor(Color.black);
157           Font f = new Font("Verdana", Font.PLAIN, 10);
158           g.setFont(f);
159           g.drawString(MessageManager.getString("label.label"), 0, 0);
160         }
161         else
162         {
163           g.setColor(maxCol);
164           g.fillRect(d.width / 2, 0, d.width / 2, d.height);
165
166         }
167       }
168     }
169
170   }
171
172   /**
173    * Shows a dialog allowing the user to create, or amend or delete, sequence
174    * features.
175    * 
176    * @param sequences
177    * @param features
178    * @param create
179    * @param ap
180    * @param featureType
181    * @return
182    */
183   boolean amendFeatures(final SequenceI[] sequences,
184           final SequenceFeature[] features, boolean create,
185           final AlignmentPanel ap, String featureType)
186   {
187     final Panel bigPanel = new Panel(new BorderLayout());
188     final TextField name = new TextField(16);
189     final TextField group = new TextField(16);
190     final TextArea description = new TextArea(3, 35);
191     final TextField start = new TextField(8);
192     final TextField end = new TextField(8);
193     final Choice overlaps;
194     Button deleteButton = new Button("Delete");
195     deleteFeature = false;
196
197     name.addTextListener(new TextListener()
198     {
199       @Override
200       public void textValueChanged(TextEvent e)
201       {
202         warnIfTypeHidden(ap.alignFrame, name.getText());
203       }
204     });
205     group.addTextListener(new TextListener()
206     {
207       @Override
208       public void textValueChanged(TextEvent e)
209       {
210         warnIfGroupHidden(ap.alignFrame, group.getText());
211       }
212     });
213     colourPanel = new FeatureColourPanel();
214     colourPanel.setSize(110, 15);
215     final FeatureRenderer fr = this;
216
217     Panel panel = new Panel(new GridLayout(3, 1));
218
219     featureIndex = 0; // feature to be amended.
220     Panel tmp;
221
222     // /////////////////////////////////////
223     // /MULTIPLE FEATURES AT SELECTED RESIDUE
224     if (!create && features.length > 1)
225     {
226       panel = new Panel(new GridLayout(4, 1));
227       tmp = new Panel();
228       tmp.add(new Label("Select Feature: "));
229       overlaps = new Choice();
230       for (int i = 0; i < features.length; i++)
231       {
232         String item = features[i].getType() + "/" + features[i].getBegin()
233                 + "-" + features[i].getEnd();
234
235         if (features[i].getFeatureGroup() != null)
236         {
237           item += " (" + features[i].getFeatureGroup() + ")";
238         }
239
240         overlaps.addItem(item);
241       }
242
243       tmp.add(overlaps);
244
245       overlaps.addItemListener(new java.awt.event.ItemListener()
246       {
247         @Override
248         public void itemStateChanged(java.awt.event.ItemEvent e)
249         {
250           int index = overlaps.getSelectedIndex();
251           if (index != -1)
252           {
253             featureIndex = index;
254             name.setText(features[index].getType());
255             description.setText(features[index].getDescription());
256             group.setText(features[index].getFeatureGroup());
257             start.setText(features[index].getBegin() + "");
258             end.setText(features[index].getEnd() + "");
259
260             SearchResultsI highlight = new SearchResults();
261             highlight.addResult(sequences[0], features[index].getBegin(),
262                     features[index].getEnd());
263
264             ap.seqPanel.seqCanvas.highlightSearchResults(highlight);
265
266           }
267           FeatureColourI col = getFeatureStyle(name.getText());
268           if (col == null)
269           {
270             Color generatedColour = ColorUtils
271                     .createColourFromName(name.getText());
272             col = new FeatureColour(generatedColour);
273           }
274
275           colourPanel.updateColor(col);
276         }
277       });
278
279       panel.add(tmp);
280     }
281     // ////////
282     // ////////////////////////////////////
283
284     tmp = new Panel();
285     panel.add(tmp);
286     tmp.add(new Label(MessageManager.getString("label.name:"), Label.RIGHT));
287     tmp.add(name);
288
289     tmp = new Panel();
290     panel.add(tmp);
291     tmp.add(new Label(MessageManager.getString("label.group:"), Label.RIGHT));
292     tmp.add(group);
293
294     tmp = new Panel();
295     panel.add(tmp);
296     tmp.add(new Label(MessageManager.getString("label.colour"), Label.RIGHT));
297     tmp.add(colourPanel);
298
299     bigPanel.add(panel, BorderLayout.NORTH);
300
301     panel = new Panel();
302     panel.add(new Label(MessageManager.getString("label.description:"),
303             Label.RIGHT));
304     panel.add(new ScrollPane().add(description));
305
306     if (!create)
307     {
308       bigPanel.add(panel, BorderLayout.SOUTH);
309
310       panel = new Panel();
311       panel.add(new Label(MessageManager.getString("label.start"),
312               Label.RIGHT));
313       panel.add(start);
314       panel.add(new Label(MessageManager.getString("label.end"),
315               Label.RIGHT));
316       panel.add(end);
317       bigPanel.add(panel, BorderLayout.CENTER);
318     }
319     else
320     {
321       bigPanel.add(panel, BorderLayout.CENTER);
322     }
323
324     if (featureType != null)
325     {
326       lastFeatureAdded = featureType;
327     }
328     else
329     {
330       if (lastFeatureAdded == null)
331       {
332         if (features[0].type != null)
333         {
334           lastFeatureAdded = features[0].type;
335         }
336         else
337         {
338           lastFeatureAdded = "feature_1";
339         }
340       }
341     }
342
343     if (lastFeatureGroupAdded == null)
344     {
345       if (features[0].featureGroup != null)
346       {
347         lastFeatureGroupAdded = features[0].featureGroup;
348       }
349       else
350       {
351         lastFeatureAdded = "Jalview";
352       }
353     }
354
355     String title = create ? MessageManager
356             .getString("label.create_new_sequence_features")
357             : MessageManager.formatMessage("label.amend_delete_features",
358                     new String[] { sequences[0].getName() });
359
360     final JVDialog dialog = new JVDialog(ap.alignFrame, title, true, 385,
361             240);
362
363     dialog.setMainPanel(bigPanel);
364
365     if (create)
366     {
367       name.setText(lastFeatureAdded);
368       group.setText(lastFeatureGroupAdded);
369     }
370     else
371     {
372       dialog.ok.setLabel(MessageManager.getString("label.amend"));
373       dialog.buttonPanel.add(deleteButton, 1);
374       deleteButton.addActionListener(new ActionListener()
375       {
376         @Override
377         public void actionPerformed(ActionEvent evt)
378         {
379           deleteFeature = true;
380           dialog.setVisible(false);
381         }
382       });
383       name.setText(features[0].getType());
384       group.setText(features[0].getFeatureGroup());
385     }
386
387     start.setText(features[0].getBegin() + "");
388     end.setText(features[0].getEnd() + "");
389     description.setText(features[0].getDescription());
390     // lookup (or generate) the feature colour
391     FeatureColourI fcol = getFeatureStyle(name.getText());
392     // simply display the feature color in a box
393     colourPanel.updateColor(fcol);
394     dialog.setResizable(true);
395     // TODO: render the graduated color in the box.
396     colourPanel.addMouseListener(new MouseAdapter()
397     {
398       @Override
399       public void mousePressed(MouseEvent evt)
400       {
401         if (!colourPanel.isGcol)
402         {
403           new UserDefinedColours(fr, ap.alignFrame);
404         }
405         else
406         {
407           new FeatureColourChooser(ap.alignFrame, name.getText());
408           dialog.transferFocus();
409         }
410       }
411     });
412     dialog.setVisible(true);
413
414     FeaturesFile ffile = new FeaturesFile();
415
416     if (dialog.accept)
417     {
418       lastFeatureAdded = name.getText().trim();
419       lastFeatureGroupAdded = group.getText().trim();
420       lastDescriptionAdded = description.getText().replace('\n', ' ');
421     }
422
423     if (lastFeatureGroupAdded != null && lastFeatureGroupAdded.length() < 1)
424     {
425       lastFeatureGroupAdded = null;
426     }
427
428     if (!create)
429     {
430
431       SequenceFeature sf = features[featureIndex];
432       if (dialog.accept)
433       {
434         boolean typeOrGroupChanged = (!lastFeatureAdded.equals(sf.type) || !lastFeatureGroupAdded
435                 .equals(sf.featureGroup));
436         sf.type = lastFeatureAdded;
437         sf.featureGroup = lastFeatureGroupAdded;
438         sf.description = lastDescriptionAdded;
439         if (!colourPanel.isGcol)
440         {
441           // update colour - otherwise its already done.
442           setColour(sf.type, new FeatureColour(colourPanel.getBackground()));
443         }
444         try
445         {
446           sf.begin = Integer.parseInt(start.getText());
447           sf.end = Integer.parseInt(end.getText());
448         } catch (NumberFormatException ex)
449         {
450         }
451
452         ffile.parseDescriptionHTML(sf, false);
453         if (typeOrGroupChanged)
454         {
455           featuresAdded();
456         }
457       }
458       if (deleteFeature)
459       {
460         sequences[0].deleteFeature(sf);
461         // ensure Feature Settings reflects removal of feature / group
462         featuresAdded();
463       }
464
465     }
466     else
467     {
468       /*
469        * adding feature(s)
470        */
471       if (dialog.accept && name.getText().length() > 0)
472       {
473         for (int i = 0; i < sequences.length; i++)
474         {
475           features[i].type = lastFeatureAdded;
476           features[i].featureGroup = lastFeatureGroupAdded;
477           features[i].description = lastDescriptionAdded;
478           sequences[i].addSequenceFeature(features[i]);
479           ffile.parseDescriptionHTML(features[i], false);
480         }
481
482         Color newColour = colourPanel.getBackground();
483         // setColour(lastFeatureAdded, fcol);
484
485         setColour(lastFeatureAdded, new FeatureColour(newColour)); // was fcol
486         featuresAdded();
487       }
488       else
489       {
490         // no update to the alignment
491         return false;
492       }
493     }
494     // refresh the alignment and the feature settings dialog
495     if (((jalview.appletgui.AlignViewport) av).featureSettings != null)
496     {
497       ((jalview.appletgui.AlignViewport) av).featureSettings.refreshTable();
498     }
499     // findAllFeatures();
500
501     ap.paintAlignment(true);
502
503     return true;
504   }
505
506   protected void warnIfGroupHidden(Frame frame, String group)
507   {
508     if (featureGroups.containsKey(group) && !featureGroups.get(group))
509     {
510       String msg = MessageManager.formatMessage("label.warning_hidden",
511               MessageManager.getString("label.group"), group);
512       showWarning(frame, msg);
513     }
514   }
515
516   protected void warnIfTypeHidden(Frame frame, String type)
517   {
518     if (getRenderOrder().contains(type))
519     {
520       if (!showFeatureOfType(type))
521       {
522         String msg = MessageManager.formatMessage("label.warning_hidden",
523                 MessageManager.getString("label.feature_type"), type);
524         showWarning(frame, msg);
525       }
526     }
527   }
528
529   /**
530    * @param frame
531    * @param msg
532    */
533   protected void showWarning(Frame frame, String msg)
534   {
535     JVDialog d = new JVDialog(frame, "", true, 350, 200);
536     Panel mp = new Panel();
537     d.ok.setLabel(MessageManager.getString("action.ok"));
538     d.cancel.setVisible(false);
539     mp.setLayout(new FlowLayout());
540     mp.add(new Label(msg));
541     d.setMainPanel(mp);
542     d.setVisible(true);
543   }
544 }