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