2 * Jalview - A Sequence Alignment Editor and Viewer
3 * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 import java.awt.event.*;
25 import java.awt.image.*;
28 import jalview.datamodel.*;
36 public class FeatureRenderer
40 float transparency = 1.0f;
44 Hashtable featureColours = new Hashtable();
46 // A higher level for grouping features of a
48 Hashtable featureGroups = null;
50 // This is actually an Integer held in the hashtable,
51 // Retrieved using the key feature type
57 * Creates a new FeatureRenderer object.
59 * @param av DOCUMENT ME!
61 public FeatureRenderer(AlignViewport av)
66 public void transferSettings(FeatureRenderer fr)
68 renderOrder = fr.renderOrder;
69 featureGroups = fr.featureGroups;
70 featureColours = fr.featureColours;
71 transparency = fr.transparency;
74 BufferedImage offscreenImage;
75 boolean offscreenRender = false;
76 public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
78 return new Color(findFeatureColour(initialCol.getRGB(),
83 * This is used by the Molecule Viewer and Overview to
84 * get the accurate colourof the rendered sequence
86 public int findFeatureColour(int initialCol, SequenceI seq, int column)
88 if (!av.showSequenceFeatures)
96 sequenceFeatures = lastSeq.getDatasetSequence().getSequenceFeatures();
97 if (sequenceFeatures == null)
102 sfSize = sequenceFeatures.length;
105 if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
107 return Color.white.getRGB();
110 //Only bother making an offscreen image if transparency is applied
111 if (transparency != 1.0f && offscreenImage == null)
113 offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
116 currentColour = null;
118 offscreenRender = true;
120 if (offscreenImage != null)
122 offscreenImage.setRGB(0, 0, initialCol);
123 drawSequence(offscreenImage.getGraphics(),
127 return offscreenImage.getRGB(0, 0);
133 lastSeq.findPosition(column),
136 if (currentColour == null)
142 return ( (Integer) currentColour).intValue();
151 * @param g DOCUMENT ME!
152 * @param seq DOCUMENT ME!
153 * @param sg DOCUMENT ME!
154 * @param start DOCUMENT ME!
155 * @param end DOCUMENT ME!
156 * @param x1 DOCUMENT ME!
157 * @param y1 DOCUMENT ME!
158 * @param width DOCUMENT ME!
159 * @param height DOCUMENT ME!
162 // SequenceFeature sf;
164 SequenceFeature[] sequenceFeatures;
165 int sfSize, sfindex, spos, epos;
167 public void drawSequence(Graphics g, SequenceI seq,
168 int start, int end, int y1)
170 if (seq.getDatasetSequence().getSequenceFeatures() == null
171 || seq.getDatasetSequence().getSequenceFeatures().length == 0)
178 fm = g.getFontMetrics();
181 if (av.featuresDisplayed == null
182 || renderOrder == null
186 if (av.featuresDisplayed.size() < 1)
191 sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
192 sfSize = sequenceFeatures.length;
195 if (lastSeq == null || seq != lastSeq)
198 sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
199 sfSize = sequenceFeatures.length;
202 if (transparency != 1 && g != null)
204 Graphics2D g2 = (Graphics2D) g;
206 AlphaComposite.getInstance(
207 AlphaComposite.SRC_OVER, transparency));
210 if (!offscreenRender)
212 spos = lastSeq.findPosition(start);
213 epos = lastSeq.findPosition(end);
217 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
219 type = renderOrder[renderIndex];
221 if (type == null || !av.featuresDisplayed.containsKey(type))
226 // loop through all features in sequence to find
227 // current feature to render
228 for (sfindex = 0; sfindex < sfSize; sfindex++)
230 if (sequenceFeatures.length <= sfindex)
234 if (!sequenceFeatures[sfindex].type.equals(type))
239 if (featureGroups != null
240 && sequenceFeatures[sfindex].featureGroup != null
242 featureGroups.containsKey(sequenceFeatures[sfindex].featureGroup)
244 ! ( (Boolean) featureGroups.get(sequenceFeatures[sfindex].
251 if (!offscreenRender && (sequenceFeatures[sfindex].getBegin() > epos
252 || sequenceFeatures[sfindex].getEnd() < spos))
257 if (offscreenRender && offscreenImage == null)
259 if (sequenceFeatures[sfindex].begin <= start &&
260 sequenceFeatures[sfindex].end >= start)
262 currentColour = av.featuresDisplayed.get(sequenceFeatures[sfindex].
266 else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
269 renderFeature(g, seq,
270 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
271 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
272 new Color( ( (Integer) av.featuresDisplayed.get(
273 sequenceFeatures[sfindex].type)).intValue()),
275 renderFeature(g, seq,
276 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
277 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
278 new Color( ( (Integer) av.featuresDisplayed.get(
279 sequenceFeatures[sfindex].type)).intValue()),
285 renderFeature(g, seq,
286 seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
287 seq.findIndex(sequenceFeatures[sfindex].end) - 1,
288 getColour(sequenceFeatures[sfindex].type),
296 if (transparency != 1.0f && g != null)
298 Graphics2D g2 = (Graphics2D) g;
300 AlphaComposite.getInstance(
301 AlphaComposite.SRC_OVER, 1.0f));
307 void renderFeature(Graphics g, SequenceI seq,
308 int fstart, int fend, Color featureColour, int start,
312 if ( ( (fstart <= end) && (fend >= start)))
315 { // fix for if the feature we have starts before the sequence start,
316 fstart = start; // but the feature end is still valid!!
323 int pady = (y1 + av.charHeight) - av.charHeight / 5;
324 for (i = fstart; i <= fend; i++)
326 s = seq.getCharAt(i);
328 if (jalview.util.Comparison.isGap(s))
333 g.setColor(featureColour);
335 g.fillRect( (i - start) * av.charWidth, y1, av.charWidth, av.charHeight);
337 if (offscreenRender || !av.validCharWidth)
342 g.setColor(Color.white);
343 charOffset = (av.charWidth - fm.charWidth(s)) / 2;
344 g.drawString(String.valueOf(s),
345 charOffset + (av.charWidth * (i - start)),
352 boolean newFeatureAdded = false;
354 public void featuresAdded()
360 boolean findingFeatures = false;
361 synchronized void findAllFeatures()
363 newFeatureAdded = false;
367 newFeatureAdded = true;
371 findingFeatures = true;
372 jalview.schemes.UserColourScheme ucs = new
373 jalview.schemes.UserColourScheme();
375 if (av.featuresDisplayed == null)
377 av.featuresDisplayed = new Hashtable();
380 av.featuresDisplayed.clear();
382 Vector allfeatures = new Vector();
383 for (int i = 0; i < av.alignment.getHeight(); i++)
385 SequenceFeature[] features
386 = av.alignment.getSequenceAt(i).getDatasetSequence().
387 getSequenceFeatures();
389 if (features == null)
395 while (index < features.length)
397 if (!av.featuresDisplayed.containsKey(features[index].getType()))
399 if (! (features[index].begin == 0 && features[index].end == 0))
401 // If beginning and end are 0, the feature is for the whole sequence
402 // and we don't want to render the feature in the normal way
404 if (getColour(features[index].getType()) == null)
406 featureColours.put(features[index].getType(),
407 ucs.createColourFromName(features[index].
411 av.featuresDisplayed.put(features[index].getType(),
412 new Integer(getColour(features[index].
413 getType()).getRGB()));
414 allfeatures.addElement(features[index].getType());
421 renderOrder = new String[allfeatures.size()];
422 Enumeration en = allfeatures.elements();
423 int i = allfeatures.size() - 1;
424 while (en.hasMoreElements())
426 renderOrder[i] = en.nextElement().toString();
430 findingFeatures = false;
433 public Color getColour(String featureType)
435 Color colour = (Color) featureColours.get(featureType);
439 static String lastFeatureAdded;
440 static String lastFeatureGroupAdded;
441 static String lastDescriptionAdded;
443 public boolean createNewFeatures(SequenceI[] sequences,
444 SequenceFeature[] features)
446 return amendFeatures(sequences, features, true, null);
449 int featureIndex = 0;
450 boolean amendFeatures(final SequenceI[] sequences,
451 final SequenceFeature[] features,
453 final AlignmentPanel ap)
455 JPanel bigPanel = new JPanel(new BorderLayout());
456 final JComboBox name = new JComboBox();
457 final JComboBox source = new JComboBox();
458 final JTextArea description = new JTextArea(3, 25);
459 final JSpinner start = new JSpinner();
460 final JSpinner end = new JSpinner();
461 start.setPreferredSize(new Dimension(80, 20));
462 end.setPreferredSize(new Dimension(80, 20));
463 final JPanel colour = new JPanel();
464 colour.setBorder(BorderFactory.createEtchedBorder());
465 colour.setMaximumSize(new Dimension(40, 10));
466 colour.addMouseListener(new MouseAdapter()
468 public void mousePressed(MouseEvent evt)
470 colour.setBackground(
471 JColorChooser.showDialog(Desktop.desktop,
472 "Select Feature Colour",
473 colour.getBackground()));
477 JPanel panel = new JPanel(new GridLayout(3, 2));
478 panel.add(new JLabel("Sequence Feature Name: ", JLabel.RIGHT));
480 panel.add(new JLabel("Feature Group: ", JLabel.RIGHT));
482 panel.add(new JLabel("Feature Colour: ", JLabel.RIGHT));
483 JPanel tmp = new JPanel();
485 colour.setPreferredSize(new Dimension(150, 15));
487 name.setEditable(true);
488 source.setEditable(true);
490 bigPanel.add(panel, BorderLayout.NORTH);
491 panel = new JPanel();
492 panel.add(new JLabel("Description: ", JLabel.RIGHT));
493 description.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
494 description.setLineWrap(true);
495 panel.add(new JScrollPane(description));
499 bigPanel.add(panel, BorderLayout.SOUTH);
501 panel = new JPanel();
502 panel.add(new JLabel(" Start:", JLabel.RIGHT));
504 panel.add(new JLabel(" End:", JLabel.RIGHT));
506 bigPanel.add(panel, BorderLayout.CENTER);
510 bigPanel.add(panel, BorderLayout.CENTER);
513 if (lastFeatureAdded == null)
515 if (features[0].type != null)
517 lastFeatureAdded = features[0].type;
521 lastFeatureAdded = "feature_1";
525 if (lastFeatureGroupAdded == null)
527 if (features[0].featureGroup != null)
529 lastFeatureGroupAdded = features[0].featureGroup;
533 lastFeatureAdded = "Jalview";
538 if (featureGroups != null)
540 en = featureGroups.keys();
541 while (en.hasMoreElements())
543 source.addItem(en.nextElement().toString());
549 if (av.featuresDisplayed != null)
551 en = av.featuresDisplayed.keys();
552 while (en.hasMoreElements())
554 name.addItem(en.nextElement().toString());
558 name.setSelectedItem(lastFeatureAdded);
559 source.setSelectedItem(lastFeatureGroupAdded);
561 lastDescriptionAdded == null ?
562 features[0].description : lastDescriptionAdded);
564 if (getColour(lastFeatureAdded) != null)
566 colour.setBackground(getColour(lastFeatureAdded));
570 colour.setBackground(new Color(60, 160, 115));
574 else if (!newFeatures)
577 for (int f = 0; f < features.length; f++)
579 name.addItem(features[f].getType().toString());
582 description.setText(features[0].getDescription());
583 source.setSelectedItem(features[0].getFeatureGroup());
584 start.setValue(new Integer(features[0].getBegin()));
585 end.setValue(new Integer(features[0].getEnd()));
586 colour.setBackground(
587 getColour(name.getSelectedItem().toString()));
588 name.addItemListener(new ItemListener()
590 public void itemStateChanged(ItemEvent e)
592 int index = name.getSelectedIndex();
595 featureIndex = index;
596 description.setText(features[index].getDescription());
597 source.setSelectedItem(features[index].getFeatureGroup());
598 start.setValue(new Integer(features[index].getBegin()));
599 end.setValue(new Integer(features[index].getEnd()));
600 colour.setBackground(
601 getColour(name.getSelectedItem().toString()));
603 SearchResults highlight = new SearchResults();
604 highlight.addResult(sequences[0],
605 features[index].getBegin(),
606 features[index].getEnd());
608 ap.seqPanel.seqCanvas.highlightSearchResults(highlight);
611 Color col = getColour(name.getSelectedItem().toString());
615 jalview.schemes.UserColourScheme()
616 .createColourFromName(name.getSelectedItem().toString());
619 colour.setBackground(col);
628 options = new Object[]
630 "Amend", "Delete", "Cancel"};
634 options = new Object[]
639 String title = newFeatures ? "Create New Sequence Feature(s)" :
640 "Amend/Delete Features for "
641 + sequences[0].getName();
643 int reply = JOptionPane.showInternalOptionDialog(Desktop.desktop,
646 JOptionPane.YES_NO_CANCEL_OPTION,
647 JOptionPane.QUESTION_MESSAGE,
651 jalview.io.FeaturesFile ffile = new jalview.io.FeaturesFile();
653 if (reply == JOptionPane.OK_OPTION
654 && name.getSelectedItem() != null
655 && source.getSelectedItem() != null)
657 //This ensures that the last sequence
658 //is refreshed and new features are rendered
660 lastFeatureAdded = name.getSelectedItem().toString();
661 lastFeatureGroupAdded = source.getSelectedItem().toString();
662 lastDescriptionAdded = description.getText().replaceAll("\n", " ");
667 SequenceFeature sf = features[featureIndex];
669 if (reply == JOptionPane.NO_OPTION)
671 sequences[0].getDatasetSequence().deleteFeature(sf);
673 else if (reply == JOptionPane.YES_OPTION)
675 sf.type = lastFeatureAdded;
676 sf.featureGroup = lastFeatureGroupAdded;
677 sf.description = lastDescriptionAdded;
678 setColour(sf.type, colour.getBackground());
681 sf.begin = ( (Integer) start.getValue()).intValue();
682 sf.end = ( (Integer) end.getValue()).intValue();
684 catch (NumberFormatException ex)
687 ffile.parseDescriptionHTML(sf, false);
692 if (reply == JOptionPane.OK_OPTION
693 && name.getSelectedItem() != null
694 && source.getSelectedItem() != null)
696 for (int i = 0; i < sequences.length; i++)
698 features[i].type = lastFeatureAdded;
699 features[i].featureGroup = lastFeatureGroupAdded;
700 features[i].description = lastDescriptionAdded;
701 sequences[i].addSequenceFeature(features[i]);
702 ffile.parseDescriptionHTML(features[i], false);
705 if (av.featuresDisplayed == null)
707 av.featuresDisplayed = new Hashtable();
710 if (featureGroups == null)
712 featureGroups = new Hashtable();
715 featureGroups.put(lastFeatureGroupAdded, new Boolean(true));
717 Color col = colour.getBackground();
718 setColour(lastFeatureAdded, colour.getBackground());
720 av.featuresDisplayed.put(lastFeatureGroupAdded,
721 new Integer(col.getRGB()));
733 if (name.getSelectedIndex() == -1)
741 public void setColour(String featureType, Color col)
743 featureColours.put(featureType, col);
746 public void setTransparency(float value)
748 transparency = value;
751 public float getTransparency()
756 public void setFeaturePriority(Object[][] data)
758 // The feature table will display high priority
759 // features at the top, but theses are the ones
760 // we need to render last, so invert the data
761 if (av.featuresDisplayed != null)
763 av.featuresDisplayed.clear();
767 av.featuresDisplayed = new Hashtable();
770 renderOrder = new String[data.length];
774 for (int i = 0; i < data.length; i++)
776 String type = data[i][0].toString();
777 setColour(type, (Color) data[i][1]);
778 if ( ( (Boolean) data[i][2]).booleanValue())
780 av.featuresDisplayed.put(type, new Integer(getColour(type).getRGB()));
783 renderOrder[data.length - i - 1] = type;