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