JAL-2541 initial unit tests for edit cut relocate features
[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.Font;
40 import java.awt.Graphics;
41 import java.awt.GridLayout;
42 import java.awt.Label;
43 import java.awt.Panel;
44 import java.awt.ScrollPane;
45 import java.awt.TextArea;
46 import java.awt.TextField;
47 import java.awt.event.ActionEvent;
48 import java.awt.event.ActionListener;
49 import java.awt.event.MouseAdapter;
50 import java.awt.event.MouseEvent;
51 import java.util.Hashtable;
52
53 /**
54  * DOCUMENT ME!
55  * 
56  * @author $author$
57  * @version $Revision$
58  */
59 public class FeatureRenderer extends
60         jalview.renderer.seqfeatures.FeatureRenderer
61 {
62
63   // Holds web links for feature groups and feature types
64   // in the form label|link
65   Hashtable featureLinks = null;
66
67   /**
68    * Creates a new FeatureRenderer object.
69    * 
70    * @param av
71    */
72   public FeatureRenderer(AlignmentViewport av)
73   {
74     super(av);
75
76   }
77
78   static String lastFeatureAdded;
79
80   static String lastFeatureGroupAdded;
81
82   static String lastDescriptionAdded;
83
84   int featureIndex = 0;
85
86   boolean deleteFeature = false;
87
88   FeatureColourPanel colourPanel;
89
90   class FeatureColourPanel extends Panel
91   {
92     String label = "";
93
94     private Color maxCol;
95
96     private boolean isColourByLabel, isGcol;
97
98     /**
99      * render a feature style in the amend feature dialog box
100      */
101     public void updateColor(FeatureColourI newcol)
102     {
103       Color bg = null;
104       String vlabel = "";
105       if (newcol.isSimpleColour())
106       {
107         bg = newcol.getColour();
108         setBackground(bg);
109       }
110       else
111       {
112         if (newcol.isAboveThreshold())
113         {
114           vlabel += " (>)";
115         }
116         else if (newcol.isBelowThreshold())
117         {
118           vlabel += " (<)";
119         }
120
121         if (isColourByLabel = newcol.isColourByLabel())
122         {
123           setBackground(bg = Color.white);
124           vlabel += " (by Label)";
125         }
126         else
127         {
128           setBackground(bg = newcol.getMinColour());
129           maxCol = newcol.getMaxColour();
130         }
131       }
132       label = vlabel;
133       setBackground(bg);
134       repaint();
135     }
136
137     FeatureColourPanel()
138     {
139       super(null);
140     }
141
142     @Override
143     public void paint(Graphics g)
144     {
145       Dimension d = getSize();
146       if (isGcol)
147       {
148         if (isColourByLabel)
149         {
150           g.setColor(Color.white);
151           g.fillRect(d.width / 2, 0, d.width / 2, d.height);
152           g.setColor(Color.black);
153           Font f = new Font("Verdana", Font.PLAIN, 10);
154           g.setFont(f);
155           g.drawString(MessageManager.getString("label.label"), 0, 0);
156         }
157         else
158         {
159           g.setColor(maxCol);
160           g.fillRect(d.width / 2, 0, d.width / 2, d.height);
161
162         }
163       }
164     }
165
166   }
167
168   /**
169    * Shows a dialog allowing the user to create, or amend or delete, sequence
170    * features.
171    * 
172    * @param sequences
173    * @param features
174    * @param create
175    * @param ap
176    * @param featureType
177    * @return
178    */
179   boolean amendFeatures(final SequenceI[] sequences,
180           final SequenceFeature[] features, boolean create,
181           final AlignmentPanel ap, String featureType)
182   {
183     Panel bigPanel = new Panel(new BorderLayout());
184     final TextField name = new TextField(16);
185     final TextField source = new TextField(16);
186     final TextArea description = new TextArea(3, 35);
187     final TextField start = new TextField(8);
188     final TextField end = new TextField(8);
189     final Choice overlaps;
190     Button deleteButton = new Button("Delete");
191     deleteFeature = false;
192
193     colourPanel = new FeatureColourPanel();
194     colourPanel.setSize(110, 15);
195     final FeatureRenderer fr = this;
196
197     Panel panel = new Panel(new GridLayout(3, 1));
198
199     featureIndex = 0; // feature to be amended.
200     Panel tmp;
201
202     // /////////////////////////////////////
203     // /MULTIPLE FEATURES AT SELECTED RESIDUE
204     if (!create && features.length > 1)
205     {
206       panel = new Panel(new GridLayout(4, 1));
207       tmp = new Panel();
208       tmp.add(new Label("Select Feature: "));
209       overlaps = new Choice();
210       for (int i = 0; i < features.length; i++)
211       {
212         String item = features[i].getType() + "/" + features[i].getBegin()
213                 + "-" + features[i].getEnd();
214
215         if (features[i].getFeatureGroup() != null)
216         {
217           item += " (" + features[i].getFeatureGroup() + ")";
218         }
219
220         overlaps.addItem(item);
221       }
222
223       tmp.add(overlaps);
224
225       overlaps.addItemListener(new java.awt.event.ItemListener()
226       {
227         @Override
228         public void itemStateChanged(java.awt.event.ItemEvent e)
229         {
230           int index = overlaps.getSelectedIndex();
231           if (index != -1)
232           {
233             featureIndex = index;
234             name.setText(features[index].getType());
235             description.setText(features[index].getDescription());
236             source.setText(features[index].getFeatureGroup());
237             start.setText(features[index].getBegin() + "");
238             end.setText(features[index].getEnd() + "");
239
240             SearchResultsI highlight = new SearchResults();
241             highlight.addResult(sequences[0], features[index].getBegin(),
242                     features[index].getEnd());
243
244             ap.seqPanel.seqCanvas.highlightSearchResults(highlight);
245
246           }
247           FeatureColourI col = getFeatureStyle(name.getText());
248           if (col == null)
249           {
250             Color generatedColour = ColorUtils
251                     .createColourFromName(name.getText());
252             col = new FeatureColour(generatedColour);
253           }
254
255           colourPanel.updateColor(col);
256         }
257       });
258
259       panel.add(tmp);
260     }
261     // ////////
262     // ////////////////////////////////////
263
264     tmp = new Panel();
265     panel.add(tmp);
266     tmp.add(new Label(MessageManager.getString("label.name:"), Label.RIGHT));
267     tmp.add(name);
268
269     tmp = new Panel();
270     panel.add(tmp);
271     tmp.add(new Label(MessageManager.getString("label.group:"), Label.RIGHT));
272     tmp.add(source);
273
274     tmp = new Panel();
275     panel.add(tmp);
276     tmp.add(new Label(MessageManager.getString("label.colour"), Label.RIGHT));
277     tmp.add(colourPanel);
278
279     bigPanel.add(panel, BorderLayout.NORTH);
280
281     panel = new Panel();
282     panel.add(new Label(MessageManager.getString("label.description:"),
283             Label.RIGHT));
284     panel.add(new ScrollPane().add(description));
285
286     if (!create)
287     {
288       bigPanel.add(panel, BorderLayout.SOUTH);
289
290       panel = new Panel();
291       panel.add(new Label(MessageManager.getString("label.start"),
292               Label.RIGHT));
293       panel.add(start);
294       panel.add(new Label(MessageManager.getString("label.end"),
295               Label.RIGHT));
296       panel.add(end);
297       bigPanel.add(panel, BorderLayout.CENTER);
298     }
299     else
300     {
301       bigPanel.add(panel, BorderLayout.CENTER);
302     }
303
304     if (featureType != null)
305     {
306       lastFeatureAdded = featureType;
307     }
308     else
309     {
310       if (lastFeatureAdded == null)
311       {
312         if (features[0].type != null)
313         {
314           lastFeatureAdded = features[0].type;
315         }
316         else
317         {
318           lastFeatureAdded = "feature_1";
319         }
320       }
321     }
322
323     if (lastFeatureGroupAdded == null)
324     {
325       if (features[0].featureGroup != null)
326       {
327         lastFeatureGroupAdded = features[0].featureGroup;
328       }
329       else
330       {
331         lastFeatureAdded = "Jalview";
332       }
333     }
334
335     String title = create ? MessageManager
336             .getString("label.create_new_sequence_features")
337             : MessageManager.formatMessage("label.amend_delete_features",
338                     new String[] { sequences[0].getName() });
339
340     final JVDialog dialog = new JVDialog(ap.alignFrame, title, true, 385,
341             240);
342
343     dialog.setMainPanel(bigPanel);
344
345     if (create)
346     {
347       name.setText(lastFeatureAdded);
348       source.setText(lastFeatureGroupAdded);
349     }
350     else
351     {
352       dialog.ok.setLabel(MessageManager.getString("label.amend"));
353       dialog.buttonPanel.add(deleteButton, 1);
354       deleteButton.addActionListener(new ActionListener()
355       {
356         @Override
357         public void actionPerformed(ActionEvent evt)
358         {
359           deleteFeature = true;
360           dialog.setVisible(false);
361         }
362       });
363       name.setText(features[0].getType());
364       source.setText(features[0].getFeatureGroup());
365     }
366
367     start.setText(features[0].getBegin() + "");
368     end.setText(features[0].getEnd() + "");
369     description.setText(features[0].getDescription());
370     // lookup (or generate) the feature colour
371     FeatureColourI fcol = getFeatureStyle(name.getText());
372     // simply display the feature color in a box
373     colourPanel.updateColor(fcol);
374     dialog.setResizable(true);
375     // TODO: render the graduated color in the box.
376     colourPanel.addMouseListener(new MouseAdapter()
377     {
378       @Override
379       public void mousePressed(MouseEvent evt)
380       {
381         if (!colourPanel.isGcol)
382         {
383           new UserDefinedColours(fr, ap.alignFrame);
384         }
385         else
386         {
387           new FeatureColourChooser(ap.alignFrame, name.getText());
388           dialog.transferFocus();
389         }
390       }
391     });
392     dialog.setVisible(true);
393
394     FeaturesFile ffile = new FeaturesFile();
395
396     if (dialog.accept)
397     {
398       lastFeatureAdded = name.getText().trim();
399       lastFeatureGroupAdded = source.getText().trim();
400       lastDescriptionAdded = description.getText().replace('\n', ' ');
401     }
402
403     if (lastFeatureGroupAdded != null && lastFeatureGroupAdded.length() < 1)
404     {
405       lastFeatureGroupAdded = null;
406     }
407
408     if (!create)
409     {
410
411       SequenceFeature sf = features[featureIndex];
412       if (dialog.accept)
413       {
414         sf.type = lastFeatureAdded;
415         sf.featureGroup = lastFeatureGroupAdded;
416         sf.description = lastDescriptionAdded;
417         if (!colourPanel.isGcol)
418         {
419           // update colour - otherwise its already done.
420           setColour(sf.type, new FeatureColour(colourPanel.getBackground()));
421         }
422         try
423         {
424           sf.begin = Integer.parseInt(start.getText());
425           sf.end = Integer.parseInt(end.getText());
426         } catch (NumberFormatException ex)
427         {
428         }
429
430         ffile.parseDescriptionHTML(sf, false);
431         setVisible(lastFeatureAdded); // if user edited name then make sure new
432                                       // type is visible
433       }
434       if (deleteFeature)
435       {
436         sequences[0].deleteFeature(sf);
437       }
438
439     }
440     else
441     {
442       if (dialog.accept && name.getText().length() > 0)
443       {
444         for (int i = 0; i < sequences.length; i++)
445         {
446           features[i].type = lastFeatureAdded;
447           features[i].featureGroup = lastFeatureGroupAdded;
448           features[i].description = lastDescriptionAdded;
449           sequences[i].addSequenceFeature(features[i]);
450           ffile.parseDescriptionHTML(features[i], false);
451         }
452
453         Color newColour = colourPanel.getBackground();
454         // setColour(lastFeatureAdded, fcol);
455
456         if (lastFeatureGroupAdded != null)
457         {
458           setGroupVisibility(lastFeatureGroupAdded, true);
459         }
460         setColour(lastFeatureAdded, new FeatureColour(newColour)); // was fcol
461         setVisible(lastFeatureAdded);
462         findAllFeatures(false); // different to original applet behaviour ?
463         // findAllFeatures();
464       }
465       else
466       {
467         // no update to the alignment
468         return false;
469       }
470     }
471     // refresh the alignment and the feature settings dialog
472     if (((jalview.appletgui.AlignViewport) av).featureSettings != null)
473     {
474       ((jalview.appletgui.AlignViewport) av).featureSettings.refreshTable();
475     }
476     // findAllFeatures();
477
478     ap.paintAlignment(true);
479
480     return true;
481   }
482 }